+++ /dev/null
-/* encodesymbolic.c
- * Copyright (C) 2014 Alexander Larsson <alexl@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <glib.h>
-#include <gdk/gdk.h>
-#include <glib/gi18n.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef G_OS_WIN32
-#include <io.h>
-#endif
-#include <errno.h>
-#include <stdlib.h>
-#include <locale.h>
-
-#include "gdkpixbufutilsprivate.h"
-
-static gchar *output_dir = NULL;
-
-static GOptionEntry args[] = {
- { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_dir, N_("Output to this directory instead of cwd"), NULL },
- { NULL }
-};
-
-int
-main (int argc, char **argv)
-{
- gchar *path, *basename, *pngpath, *pngfile, *dot;
- GOptionContext *context;
- GdkPixbuf *symbolic;
- GError *error;
- int width, height;
- gchar **sizev;
- GFileOutputStream *out;
- GFile *dest;
- char *data;
- gsize len;
-
- setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
- bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
- g_set_prgname ("gtk-encode-symbolic-svg");
-
- context = g_option_context_new ("PATH WIDTHxHEIGHT");
- g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
-
- g_option_context_parse (context, &argc, &argv, NULL);
-
- if (argc < 3)
- {
- g_printerr ("%s\n", g_option_context_get_help (context, FALSE, NULL));
- return 1;
- }
-
- width = 0;
- height = 0;
- sizev = g_strsplit (argv[2], "x", 0);
- if (g_strv_length (sizev) == 2)
- {
- width = atoi(sizev[0]);
- height = atoi(sizev[1]);
- }
- g_strfreev (sizev);
-
- if (width == 0 || height == 0)
- {
- g_printerr (_("Invalid size %s\n"), argv[2]);
- return 1;
- }
-
- path = argv[1];
-#ifdef G_OS_WIN32
- path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
-#endif
-
- error = NULL;
- if (!g_file_get_contents (path, &data, &len, &error))
- {
- g_printerr (_("Can’t load file: %s\n"), error->message);
- return 1;
- }
-
- symbolic = gtk_make_symbolic_pixbuf_from_data (data, len, width, height, 1.0, &error);
- if (symbolic == NULL)
- {
- g_printerr (_("Can’t load file: %s\n"), error->message);
- return 1;
- }
-
- g_free (data);
-
- basename = g_path_get_basename (path);
-
- dot = strrchr (basename, '.');
- if (dot != NULL)
- *dot = 0;
- pngfile = g_strconcat (basename, ".symbolic.png", NULL);
- g_free (basename);
-
- if (output_dir != NULL)
- pngpath = g_build_filename (output_dir, pngfile, NULL);
- else
- pngpath = g_strdup (pngfile);
-
- g_free (pngfile);
-
- dest = g_file_new_for_path (pngpath);
-
-
- out = g_file_replace (dest,
- NULL, FALSE,
- G_FILE_CREATE_REPLACE_DESTINATION,
- NULL, &error);
- if (out == NULL)
- {
- g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
- return 1;
- }
-
- if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL))
- {
- g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
- return 1;
- }
-
- if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, &error))
- {
- g_printerr (_("Can’t close stream"));
- return 1;
- }
-
- g_object_unref (out);
- g_free (pngpath);
-
- return 0;
-}
+++ /dev/null
-/* Copyright (C) 2016 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <gdk/gdk.h>
-#include "gdkpixbufutilsprivate.h"
-
-static GdkPixbuf *
-load_from_stream (GdkPixbufLoader *loader,
- GInputStream *stream,
- GCancellable *cancellable,
- GError **error)
-{
- GdkPixbuf *pixbuf;
- gssize n_read;
- guchar buffer[65536];
- gboolean res;
-
- res = TRUE;
- while (1)
- {
- n_read = g_input_stream_read (stream, buffer, sizeof (buffer), cancellable, error);
- if (n_read < 0)
- {
- res = FALSE;
- error = NULL; /* Ignore further errors */
- break;
- }
-
- if (n_read == 0)
- break;
-
- if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error))
- {
- res = FALSE;
- error = NULL;
- break;
- }
- }
-
- if (!gdk_pixbuf_loader_close (loader, error))
- {
- res = FALSE;
- error = NULL;
- }
-
- pixbuf = NULL;
-
- if (res)
- {
- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
- if (pixbuf)
- g_object_ref (pixbuf);
- }
-
- return pixbuf;
-}
-
-static void
-size_prepared_cb (GdkPixbufLoader *loader,
- gint width,
- gint height,
- gpointer data)
-{
- gdouble *scale = data;
-
- width = MAX (*scale * width, 1);
- height = MAX (*scale * height, 1);
-
- gdk_pixbuf_loader_set_size (loader, width, height);
-}
-
-/* Like gdk_pixbuf_new_from_stream_at_scale, but
- * load the image at its original size times the
- * given scale.
- */
-GdkPixbuf *
-_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
- gdouble scale,
- GCancellable *cancellable,
- GError **error)
-{
- GdkPixbufLoader *loader;
- GdkPixbuf *pixbuf;
-
- loader = gdk_pixbuf_loader_new ();
-
- g_signal_connect (loader, "size-prepared",
- G_CALLBACK (size_prepared_cb), &scale);
-
- pixbuf = load_from_stream (loader, stream, cancellable, error);
-
- g_object_unref (loader);
-
- return pixbuf;
-}
-
-/* Like gdk_pixbuf_new_from_resource_at_scale, but
- * load the image at its original size times the
- * given scale.
- */
-GdkPixbuf *
-_gdk_pixbuf_new_from_resource_scaled (const gchar *resource_path,
- gdouble scale,
- GError **error)
-{
- GInputStream *stream;
- GdkPixbuf *pixbuf;
-
- stream = g_resources_open_stream (resource_path, 0, error);
- if (stream == NULL)
- return NULL;
-
- pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, scale, NULL, error);
- g_object_unref (stream);
-
- return pixbuf;
-}
-
-static GdkPixbuf *
-load_symbolic_svg (const char *file_data,
- gsize file_len,
- int width,
- int height,
- double scale,
- const GdkRGBA *fg,
- const GdkRGBA *success_color,
- const GdkRGBA *warning_color,
- const GdkRGBA *error_color,
- GError **error)
-{
- GInputStream *stream;
- GdkPixbuf *pixbuf;
- gchar *css_fg;
- gchar *css_success;
- gchar *css_warning;
- gchar *css_error;
- gchar *data;
- gchar *svg_width, *svg_height;
- gchar *escaped_file_data;
-
- css_fg = gdk_rgba_to_string (fg);
-
- css_success = css_warning = css_error = NULL;
-
- css_warning = gdk_rgba_to_string (warning_color);
- css_error = gdk_rgba_to_string (error_color);
- css_success = gdk_rgba_to_string (success_color);
-
- /* Fetch size from the original icon */
- stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
- pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
- g_object_unref (stream);
-
- if (!pixbuf)
- return NULL;
-
- if (width == 0)
- width = gdk_pixbuf_get_width (pixbuf) * scale;
- if (height == 0)
- height = gdk_pixbuf_get_height (pixbuf) * scale;
-
- svg_width = g_strdup_printf ("%d", gdk_pixbuf_get_width (pixbuf));
- svg_height = g_strdup_printf ("%d", gdk_pixbuf_get_height (pixbuf));
- g_object_unref (pixbuf);
-
- escaped_file_data = g_markup_escape_text (file_data, file_len);
-
- data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
- "<svg version=\"1.1\"\n"
- " xmlns=\"http://www.w3.org/2000/svg\"\n"
- " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
- " width=\"", svg_width, "\"\n"
- " height=\"", svg_height, "\">\n"
- " <style type=\"text/css\">\n"
- " rect,circle,path {\n"
- " fill: ", css_fg," !important;\n"
- " }\n"
- " .warning {\n"
- " fill: ", css_warning, " !important;\n"
- " }\n"
- " .error {\n"
- " fill: ", css_error ," !important;\n"
- " }\n"
- " .success {\n"
- " fill: ", css_success, " !important;\n"
- " }\n"
- " </style>\n"
- " <xi:include href=\"data:text/xml,", escaped_file_data, "\"/>\n"
- "</svg>",
- NULL);
- g_free (escaped_file_data);
- g_free (css_fg);
- g_free (css_warning);
- g_free (css_error);
- g_free (css_success);
- g_free (svg_width);
- g_free (svg_height);
-
- stream = g_memory_input_stream_new_from_data (data, -1, g_free);
- pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error);
- g_object_unref (stream);
-
- return pixbuf;
-}
-
-static void
-extract_plane (GdkPixbuf *src,
- GdkPixbuf *dst,
- int from_plane,
- int to_plane)
-{
- guchar *src_data, *dst_data;
- int width, height, src_stride, dst_stride;
- guchar *src_row, *dst_row;
- int x, y;
-
- width = gdk_pixbuf_get_width (src);
- height = gdk_pixbuf_get_height (src);
-
- g_assert (width <= gdk_pixbuf_get_width (dst));
- g_assert (height <= gdk_pixbuf_get_height (dst));
-
- src_stride = gdk_pixbuf_get_rowstride (src);
- src_data = gdk_pixbuf_get_pixels (src);
-
- dst_data = gdk_pixbuf_get_pixels (dst);
- dst_stride = gdk_pixbuf_get_rowstride (dst);
-
- for (y = 0; y < height; y++)
- {
- src_row = src_data + src_stride * y;
- dst_row = dst_data + dst_stride * y;
- for (x = 0; x < width; x++)
- {
- dst_row[to_plane] = src_row[from_plane];
- src_row += 4;
- dst_row += 4;
- }
- }
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_data (const char *file_data,
- gsize file_len,
- int width,
- int height,
- double scale,
- GError **error)
-
-{
- GdkRGBA r = { 1,0,0,1}, g = {0,1,0,1};
- GdkPixbuf *loaded;
- GdkPixbuf *pixbuf = NULL;
- int plane;
-
- for (plane = 0; plane < 3; plane++)
- {
- /* Here we render the svg with all colors solid, this should
- * always make the alpha channel the same and it should match
- * the final alpha channel for all possible renderings. We
- * Just use it as-is for final alpha.
- *
- * For the 3 non-fg colors, we render once each with that
- * color as red, and every other color as green. The resulting
- * red will describe the amount of that color is in the
- * opaque part of the color. We store these as the rgb
- * channels, with the color of the fg being implicitly
- * the "rest", as all color fractions should add up to 1.
- */
- loaded = load_symbolic_svg (file_data, file_len, width, height, scale,
- &g,
- plane == 0 ? &r : &g,
- plane == 1 ? &r : &g,
- plane == 2 ? &r : &g,
- error);
- if (loaded == NULL)
- return NULL;
-
- if (pixbuf == NULL)
- {
- pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
- gdk_pixbuf_get_width (loaded),
- gdk_pixbuf_get_height (loaded));
- gdk_pixbuf_fill (pixbuf, 0);
- }
-
- if (plane == 0)
- extract_plane (loaded, pixbuf, 3, 3);
-
- extract_plane (loaded, pixbuf, 0, plane);
-
- g_object_unref (loaded);
- }
-
- return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_resource (const char *path,
- int width,
- int height,
- double scale,
- GError **error)
-{
- GBytes *bytes;
- const char *data;
- gsize size;
- GdkPixbuf *pixbuf;
-
- bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
- if (bytes == NULL)
- return NULL;
-
- data = g_bytes_get_data (bytes, &size);
-
- pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, error);
-
- g_bytes_unref (bytes);
-
- return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_file (GFile *file,
- int width,
- int height,
- double scale,
- GError **error)
-{
- char *data;
- gsize size;
- GdkPixbuf *pixbuf;
-
- if (!g_file_load_contents (file, NULL, &data, &size, NULL, error))
- return NULL;
-
- pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, error);
-
- g_free (data);
-
- return pixbuf;
-}
-
-GdkTexture *
-gtk_make_symbolic_texture_from_resource (const char *path,
- int width,
- int height,
- double scale,
- GError **error)
-{
- GdkPixbuf *pixbuf;
- GdkTexture *texture;
-
- pixbuf = gtk_make_symbolic_pixbuf_from_resource (path, width, height, scale, error);
- texture = gdk_texture_new_for_pixbuf (pixbuf);
- g_object_unref (pixbuf);
-
- return texture;
-}
-
-GdkTexture *
-gtk_make_symbolic_texture_from_file (GFile *file,
- int width,
- int height,
- double scale,
- GError **error)
-{
- GdkPixbuf *pixbuf;
- GdkTexture *texture;
-
- pixbuf = gtk_make_symbolic_pixbuf_from_file (file, width, height, scale, error);
- texture = gdk_texture_new_for_pixbuf (pixbuf);
- g_object_unref (pixbuf);
-
- return texture;
-}
+++ /dev/null
-/* Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-
-
-typedef struct {
- GtkBuilder *builder;
- GList *classes;
- gboolean packing;
- gboolean packing_started;
- gboolean cell_packing;
- gboolean cell_packing_started;
- gint in_child;
- gint child_started;
- gchar **attribute_names;
- gchar **attribute_values;
- GString *value;
- gboolean unclosed_starttag;
- gint indent;
- char *input_filename;
- char *output_filename;
- FILE *output;
-} MyParserData;
-
-static void
-canonicalize_key (gchar *key)
-{
- gchar *p;
-
- for (p = key; *p != 0; p++)
- {
- gchar c = *p;
-
- /* We may meet something like AtkObject::accessible-name */
- if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':'))
- continue;
-
- if (c != '-' &&
- (c < '0' || c > '9') &&
- (c < 'A' || c > 'Z') &&
- (c < 'a' || c > 'z'))
- *p = '-';
- }
-}
-
-static GParamSpec *
-get_property_pspec (MyParserData *data,
- const gchar *class_name,
- const gchar *property_name)
-{
- GType type;
- GObjectClass *class;
- GParamSpec *pspec;
- gchar *canonical_name;
-
- type = g_type_from_name (class_name);
- if (type == G_TYPE_INVALID)
- {
- GtkBuilder *builder = gtk_builder_new ();
- type = gtk_builder_get_type_from_name (builder, class_name);
- g_object_unref (builder);
- if (type == G_TYPE_INVALID)
- return NULL;
- }
-
- class = g_type_class_ref (type);
- canonical_name = g_strdup (property_name);
- canonicalize_key (canonical_name);
- if (data->packing)
- pspec = gtk_container_class_find_child_property (class, canonical_name);
- else if (data->cell_packing)
- {
- GObjectClass *cell_class;
-
- /* We're just assuming that the cell layout is using a GtkCellAreaBox. */
- cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX);
- pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name);
- g_type_class_unref (cell_class);
- }
- else
- pspec = g_object_class_find_property (class, canonical_name);
- g_free (canonical_name);
- g_type_class_unref (class);
-
- return pspec;
-}
-
-
-static gboolean
-value_is_default (MyParserData *data,
- const gchar *class_name,
- const gchar *property_name,
- const gchar *value_string)
-{
- GValue value = { 0, };
- gboolean ret;
- GError *error = NULL;
- GParamSpec *pspec;
-
- pspec = get_property_pspec (data, class_name, property_name);
-
- if (pspec == NULL)
- {
- if (data->packing)
- g_printerr (_("Packing property %s::%s not found\n"), class_name, property_name);
- else if (data->cell_packing)
- g_printerr (_("Cell property %s::%s not found\n"), class_name, property_name);
- else
- g_printerr (_("Property %s::%s not found\n"), class_name, property_name);
- return FALSE;
- }
- else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
- return FALSE;
-
- if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error))
- {
- g_printerr (_("Couldn’t parse value for %s::%s: %s\n"), class_name, property_name, error->message);
- g_error_free (error);
- ret = FALSE;
- }
- else
- ret = g_param_value_defaults (pspec, &value);
-
- g_value_reset (&value);
-
- return ret;
-}
-
-static gboolean
-property_is_boolean (MyParserData *data,
- const gchar *class_name,
- const gchar *property_name)
-{
- GParamSpec *pspec;
-
- pspec = get_property_pspec (data, class_name, property_name);
- if (pspec)
- return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN;
-
- return FALSE;
-}
-
-static const gchar *
-canonical_boolean_value (MyParserData *data,
- const gchar *string)
-{
- GValue value = G_VALUE_INIT;
- gboolean b = FALSE;
-
- if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL))
- b = g_value_get_boolean (&value);
-
- return b ? "1" : "0";
-}
-
-/* A number of properties unfortunately can't be omitted even
- * if they are nominally set to their default value. In many
- * cases, this is due to subclasses not overriding the default
- * value from the superclass.
- */
-static gboolean
-needs_explicit_setting (MyParserData *data,
- const gchar *class_name,
- const gchar *property_name)
-{
- struct _Prop {
- const char *class;
- const char *property;
- gboolean packing;
- } props[] = {
- { "GtkAboutDialog", "program-name", 0 },
- { "GtkCalendar", "year", 0 },
- { "GtkCalendar", "month", 0 },
- { "GtkCalendar", "day", 0 },
- { "GtkPlacesSidebar", "show-desktop", 0 },
- { "GtkRadioButton", "draw-indicator", 0 },
- { "GtkGrid", "left-attach", 1 },
- { "GtkGrid", "top-attach", 1 },
- { "GtkWidget", "hexpand", 0 },
- { "GtkWidget", "vexpand", 0 },
- { NULL, NULL, 0 }
- };
- gchar *canonical_name;
- gboolean found;
- gint k;
-
- canonical_name = g_strdup (property_name);
- g_strdelimit (canonical_name, "_", '-');
-
- found = FALSE;
- for (k = 0; props[k].class; k++)
- {
- if (strcmp (class_name, props[k].class) == 0 &&
- strcmp (canonical_name, props[k].property) == 0 &&
- data->packing == props[k].packing)
- {
- found = TRUE;
- break;
- }
- }
-
- g_free (canonical_name);
-
- return found;
-}
-
-static void
-maybe_start_packing (MyParserData *data)
-{
- if (data->packing)
- {
- if (!data->packing_started)
- {
- g_fprintf (data->output, "%*s<packing>\n", data->indent, "");
- data->indent += 2;
- data->packing_started = TRUE;
- }
- }
-}
-
-static void
-maybe_start_cell_packing (MyParserData *data)
-{
- if (data->cell_packing)
- {
- if (!data->cell_packing_started)
- {
- g_fprintf (data->output, "%*s<cell-packing>\n", data->indent, "");
- data->indent += 2;
- data->cell_packing_started = TRUE;
- }
- }
-}
-
-static void
-maybe_start_child (MyParserData *data)
-{
- if (data->in_child > 0)
- {
- if (data->child_started < data->in_child)
- {
- g_fprintf (data->output, "%*s<child>\n", data->indent, "");
- data->indent += 2;
- data->child_started += 1;
- }
- }
-}
-
-static void
-maybe_emit_property (MyParserData *data)
-{
- gint i;
- gboolean bound;
- gboolean translatable;
- gchar *escaped;
- const gchar *class_name;
- const gchar *property_name;
- const gchar *value_string;
-
- class_name = (const gchar *)data->classes->data;
- property_name = "";
- value_string = (const gchar *)data->value->str;
-
- bound = FALSE;
- translatable = FALSE;
- for (i = 0; data->attribute_names[i]; i++)
- {
- if (strcmp (data->attribute_names[i], "bind-source") == 0 ||
- strcmp (data->attribute_names[i], "bind_source") == 0)
- bound = TRUE;
- else if (strcmp (data->attribute_names[i], "translatable") == 0)
- translatable = TRUE;
- else if (strcmp (data->attribute_names[i], "name") == 0)
- property_name = (const gchar *)data->attribute_values[i];
- }
-
- if (!translatable &&
- !bound &&
- !needs_explicit_setting (data, class_name, property_name))
- {
- for (i = 0; data->attribute_names[i]; i++)
- {
- if (strcmp (data->attribute_names[i], "name") == 0)
- {
- if (data->classes == NULL)
- break;
-
- if (value_is_default (data, class_name, property_name, value_string))
- return;
- }
- }
- }
-
- maybe_start_packing (data);
- maybe_start_cell_packing (data);
-
- g_fprintf (data->output, "%*s<property", data->indent, "");
- for (i = 0; data->attribute_names[i]; i++)
- {
- if (!translatable &&
- (strcmp (data->attribute_names[i], "comments") == 0 ||
- strcmp (data->attribute_names[i], "context") == 0))
- continue;
-
- escaped = g_markup_escape_text (data->attribute_values[i], -1);
-
- if (strcmp (data->attribute_names[i], "name") == 0)
- canonicalize_key (escaped);
-
- g_fprintf (data->output, " %s=\"%s\"", data->attribute_names[i], escaped);
- g_free (escaped);
- }
-
- if (bound)
- {
- g_fprintf (data->output, "/>\n");
- }
- else
- {
- g_fprintf (data->output, ">");
- if (property_is_boolean (data, class_name, property_name))
- {
- g_fprintf (data->output, "%s", canonical_boolean_value (data, value_string));
- }
- else
- {
- escaped = g_markup_escape_text (value_string, -1);
- g_fprintf (data->output, "%s", escaped);
- g_free (escaped);
- }
- g_fprintf (data->output, "</property>\n");
- }
-}
-
-static void
-maybe_close_starttag (MyParserData *data)
-{
- if (data->unclosed_starttag)
- {
- g_fprintf (data->output, ">\n");
- data->unclosed_starttag = FALSE;
- }
-}
-
-static gboolean
-stack_is (GMarkupParseContext *context,
- ...)
-{
- va_list args;
- gchar *s, *p;
- const GSList *stack;
-
- stack = g_markup_parse_context_get_element_stack (context);
-
- va_start (args, context);
- s = va_arg (args, gchar *);
- while (s)
- {
- if (stack == NULL)
- {
- va_end (args);
- return FALSE;
- }
-
- p = (gchar *)stack->data;
- if (strcmp (s, p) != 0)
- {
- va_end (args);
- return FALSE;
- }
-
- s = va_arg (args, gchar *);
- stack = stack->next;
- }
-
- va_end (args);
- return TRUE;
-}
-
-static void
-start_element (GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data,
- GError **error)
-{
- gint i;
- gchar *escaped;
- MyParserData *data = user_data;
-
- maybe_close_starttag (data);
-
- if (strcmp (element_name, "property") == 0)
- {
- g_assert (data->attribute_names == NULL);
- g_assert (data->attribute_values == NULL);
- g_assert (data->value == NULL);
-
- data->attribute_names = g_strdupv ((gchar **)attribute_names);
- data->attribute_values = g_strdupv ((gchar **)attribute_values);
- data->value = g_string_new ("");
-
- return;
- }
- else if (strcmp (element_name, "packing") == 0)
- {
- data->packing = TRUE;
- data->packing_started = FALSE;
-
- return;
- }
- else if (strcmp (element_name, "cell-packing") == 0)
- {
- data->cell_packing = TRUE;
- data->cell_packing_started = FALSE;
-
- return;
- }
- else if (strcmp (element_name, "child") == 0)
- {
- data->in_child += 1;
-
- if (attribute_names[0] == NULL)
- return;
-
- data->child_started += 1;
- }
- else if (strcmp (element_name, "attribute") == 0)
- {
- /* attribute in label has no content */
- if (data->classes == NULL ||
- strcmp ((gchar *)data->classes->data, "GtkLabel") != 0)
- data->value = g_string_new ("");
- }
- else if (stack_is (context, "item", "items", NULL) ||
- stack_is (context, "action-widget", "action-widgets", NULL) ||
- stack_is (context, "mime-type", "mime-types", NULL) ||
- stack_is (context, "pattern", "patterns", NULL) ||
- stack_is (context, "application", "applications", NULL) ||
- stack_is (context, "col", "row", "data", NULL) ||
- stack_is (context, "mark", "marks", NULL) ||
- stack_is (context, "action", "accessibility", NULL))
- {
- data->value = g_string_new ("");
- }
- else if (strcmp (element_name, "placeholder") == 0)
- {
- return;
- }
- else if (strcmp (element_name, "object") == 0 ||
- strcmp (element_name, "template") == 0)
- {
- maybe_start_child (data);
-
- for (i = 0; attribute_names[i]; i++)
- {
- if (strcmp (attribute_names[i], "class") == 0)
- {
- data->classes = g_list_prepend (data->classes,
- g_strdup (attribute_values[i]));
- break;
- }
- }
- }
-
- g_fprintf (data->output, "%*s<%s", data->indent, "", element_name);
- for (i = 0; attribute_names[i]; i++)
- {
- escaped = g_markup_escape_text (attribute_values[i], -1);
- g_fprintf (data->output, " %s=\"%s\"", attribute_names[i], escaped);
- g_free (escaped);
- }
- data->unclosed_starttag = TRUE;
- data->indent += 2;
-}
-
-static void
-end_element (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
- MyParserData *data = user_data;
-
- if (strcmp (element_name, "property") == 0)
- {
- maybe_emit_property (data);
-
- g_clear_pointer (&data->attribute_names, g_strfreev);
- g_clear_pointer (&data->attribute_values, g_strfreev);
- g_string_free (data->value, TRUE);
- data->value = NULL;
- return;
- }
- else if (strcmp (element_name, "packing") == 0)
- {
- data->packing = FALSE;
- if (!data->packing_started)
- return;
- }
- else if (strcmp (element_name, "cell-packing") == 0)
- {
- data->cell_packing = FALSE;
- if (!data->cell_packing_started)
- return;
- }
- else if (strcmp (element_name, "child") == 0)
- {
- data->in_child -= 1;
- if (data->child_started == data->in_child)
- return;
- data->child_started -= 1;
- }
- else if (strcmp (element_name, "placeholder") == 0)
- {
- return;
- }
- else if (strcmp (element_name, "object") == 0 ||
- strcmp (element_name, "template") == 0)
- {
- g_free (data->classes->data);
- data->classes = g_list_delete_link (data->classes, data->classes);
- }
-
- if (data->value != NULL)
- {
- gchar *escaped;
-
- if (data->unclosed_starttag)
- g_fprintf (data->output, ">");
-
- escaped = g_markup_escape_text (data->value->str, -1);
- g_fprintf (data->output, "%s</%s>\n", escaped, element_name);
- g_free (escaped);
-
- g_string_free (data->value, TRUE);
- data->value = NULL;
- }
- else
- {
- if (data->unclosed_starttag)
- g_fprintf (data->output, "/>\n");
- else
- g_fprintf (data->output, "%*s</%s>\n", data->indent - 2, "", element_name);
- }
-
- data->indent -= 2;
- data->unclosed_starttag = FALSE;
-}
-
-static void
-text (GMarkupParseContext *context,
- const gchar *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
-{
- MyParserData *data = user_data;
-
- if (data->value)
- {
- g_string_append_len (data->value, text, text_len);
- return;
- }
-}
-
-static void
-passthrough (GMarkupParseContext *context,
- const gchar *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
-{
- MyParserData *data = user_data;
-
- maybe_close_starttag (data);
-
- g_fprintf (data->output, "%*s%s\n", data->indent, "", text);
-}
-
-GMarkupParser parser = {
- start_element,
- end_element,
- text,
- passthrough,
- NULL
-};
-
-static void
-do_simplify (int *argc,
- const char ***argv)
-{
- GMarkupParseContext *context;
- gchar *buffer;
- MyParserData data;
- gboolean replace = FALSE;
- char **filenames = NULL;
- GOptionContext *ctx;
- const GOptionEntry entries[] = {
- { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
- { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
- { NULL, }
- };
- GError *error = NULL;
-
- ctx = g_option_context_new (NULL);
- g_option_context_set_help_enabled (ctx, FALSE);
- g_option_context_add_main_entries (ctx, entries, NULL);
-
- if (!g_option_context_parse (ctx, argc, (char ***)argv, &error))
- {
- g_printerr ("%s\n", error->message);
- g_error_free (error);
- exit (1);
- }
-
- g_option_context_free (ctx);
-
- if (filenames == NULL)
- {
- g_printerr ("No .ui file specified\n");
- exit (1);
- }
-
- if (g_strv_length (filenames) > 1)
- {
- g_printerr ("Can only simplify a single .ui file\n");
- exit (1);
- }
-
- data.input_filename = filenames[0];
- data.output_filename = NULL;
-
- if (replace)
- {
- int fd;
- fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL);
- data.output = fdopen (fd, "w");
- }
- else
- {
- data.output = stdout;
- }
-
- if (!g_file_get_contents (filenames[0], &buffer, NULL, &error))
- {
- g_printerr (_("Can’t load file: %s\n"), error->message);
- exit (1);
- }
-
- data.builder = gtk_builder_new ();
- data.classes = NULL;
- data.attribute_names = NULL;
- data.attribute_values = NULL;
- data.value = NULL;
- data.packing = FALSE;
- data.packing_started = FALSE;
- data.cell_packing = FALSE;
- data.cell_packing_started = FALSE;
- data.in_child = 0;
- data.child_started = 0;
- data.unclosed_starttag = FALSE;
- data.indent = 0;
-
- context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL);
- if (!g_markup_parse_context_parse (context, buffer, -1, &error))
- {
- g_printerr (_("Can’t parse file: %s\n"), error->message);
- exit (1);
- }
-
- fclose (data.output);
-
- if (data.output_filename)
- {
- char *content;
- gsize length;
-
- if (!g_file_get_contents (data.output_filename, &content, &length, &error))
- {
- g_printerr ("Failed to read %s: %s\n", data.output_filename, error->message);
- exit (1);
- }
-
- if (!g_file_set_contents (data.input_filename, content, length, &error))
- {
- g_printerr ("Failed to write %s: %s\n", data.input_filename, error->message);
- exit (1);
- }
- }
-}
-
-static GType
-make_fake_type (const gchar *type_name,
- const gchar *parent_name)
-{
- GType parent_type;
- GTypeQuery query;
-
- parent_type = g_type_from_name (parent_name);
- if (parent_type == G_TYPE_INVALID)
- {
- g_printerr ("Failed to lookup template parent type %s\n", parent_name);
- exit (1);
- }
-
- g_type_query (parent_type, &query);
- return g_type_register_static_simple (parent_type,
- type_name,
- query.class_size,
- NULL,
- query.instance_size,
- NULL,
- 0);
-}
-
-static void
-do_validate_template (const gchar *filename,
- const gchar *type_name,
- const gchar *parent_name)
-{
- GType template_type;
- GtkWidget *widget;
- GtkBuilder *builder;
- GError *error = NULL;
- gint ret;
-
- /* Only make a fake type if it doesn't exist yet.
- * This lets us e.g. validate the GtkFileChooserWidget template.
- */
- template_type = g_type_from_name (type_name);
- if (template_type == G_TYPE_INVALID)
- template_type = make_fake_type (type_name, parent_name);
-
- widget = g_object_new (template_type, NULL);
- if (!widget)
- {
- g_printerr ("Failed to create an instance of the template type %s\n", type_name);
- exit (1);
- }
-
- builder = gtk_builder_new ();
- ret = gtk_builder_extend_with_template (builder, widget, template_type, " ", 1, &error);
- if (ret)
- ret = gtk_builder_add_from_file (builder, filename, &error);
- g_object_unref (builder);
-
- if (ret == 0)
- {
- g_printerr ("%s\n", error->message);
- exit (1);
- }
-}
-
-static gboolean
-parse_template_error (const gchar *message,
- gchar **class_name,
- gchar **parent_name)
-{
- gchar *p;
-
- if (!strstr (message, "Not expecting to handle a template"))
- return FALSE;
-
- p = strstr (message, "(class '");
- if (p)
- {
- *class_name = g_strdup (p + strlen ("(class '"));
- p = strstr (*class_name, "'");
- if (p)
- *p = '\0';
- }
- p = strstr (message, ", parent '");
- if (p)
- {
- *parent_name = g_strdup (p + strlen (", parent '"));
- p = strstr (*parent_name, "'");
- if (p)
- *p = '\0';
- }
-
- return TRUE;
-}
-
-static void
-do_validate (const gchar *filename)
-{
- GtkBuilder *builder;
- GError *error = NULL;
- gint ret;
- gchar *class_name = NULL;
- gchar *parent_name = NULL;
-
- builder = gtk_builder_new ();
- ret = gtk_builder_add_from_file (builder, filename, &error);
- g_object_unref (builder);
-
- if (ret == 0)
- {
- if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) &&
- parse_template_error (error->message, &class_name, &parent_name))
- {
- do_validate_template (filename, class_name, parent_name);
- }
- else
- {
- g_printerr ("%s\n", error->message);
- exit (1);
- }
- }
-}
-
-static const gchar *
-object_get_name (GObject *object)
-{
- if (GTK_IS_BUILDABLE (object))
- return gtk_buildable_get_name (GTK_BUILDABLE (object));
- else
- return g_object_get_data (object, "gtk-builder-name");
-}
-
-static void
-do_enumerate (const gchar *filename)
-{
- GtkBuilder *builder;
- GError *error = NULL;
- gint ret;
- GSList *list, *l;
- GObject *object;
- const gchar *name;
-
- builder = gtk_builder_new ();
- ret = gtk_builder_add_from_file (builder, filename, &error);
-
- if (ret == 0)
- {
- g_printerr ("%s\n", error->message);
- exit (1);
- }
-
- list = gtk_builder_get_objects (builder);
- for (l = list; l; l = l->next)
- {
- object = l->data;
- name = object_get_name (object);
- if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
- continue;
-
- g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object));
- }
- g_slist_free (list);
-
- g_object_unref (builder);
-}
-
-static void
-set_window_title (GtkWindow *window,
- const char *filename,
- const char *id)
-{
- gchar *name;
- gchar *title;
-
- name = g_path_get_basename (filename);
-
- if (id)
- title = g_strdup_printf ("%s in %s", id, name);
- else
- title = g_strdup (name);
-
- gtk_window_set_title (window, title);
-
- g_free (title);
- g_free (name);
-}
-
-static void
-preview_file (const char *filename,
- const char *id,
- const char *cssfile)
-{
- GtkBuilder *builder;
- GError *error = NULL;
- GObject *object;
- GtkWidget *window;
-
- if (cssfile)
- {
- GtkCssProvider *provider;
-
- provider = gtk_css_provider_new ();
- gtk_css_provider_load_from_path (provider, cssfile);
-
- gtk_style_context_add_provider_for_display (gdk_display_get_default (),
- GTK_STYLE_PROVIDER (provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
- }
-
- builder = gtk_builder_new ();
- if (!gtk_builder_add_from_file (builder, filename, &error))
- {
- g_printerr ("%s\n", error->message);
- exit (1);
- }
-
- object = NULL;
-
- if (id)
- {
- object = gtk_builder_get_object (builder, id);
- }
- else
- {
- GSList *objects, *l;
-
- objects = gtk_builder_get_objects (builder);
- for (l = objects; l; l = l->next)
- {
- GObject *obj = l->data;
-
- if (GTK_IS_WINDOW (obj))
- {
- object = obj;
- break;
- }
- else if (GTK_IS_WIDGET (obj))
- {
- if (object == NULL)
- object = obj;
- }
- }
- g_slist_free (objects);
- }
-
- if (object == NULL)
- {
- if (id)
- g_printerr ("No object with ID '%s' found\n", id);
- else
- g_printerr ("No previewable object found\n");
- exit (1);
- }
-
- if (!GTK_IS_WIDGET (object))
- {
- g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object));
- exit (1);
- }
-
- if (GTK_IS_WINDOW (object))
- window = GTK_WIDGET (object);
- else
- {
- GtkWidget *widget = GTK_WIDGET (object);
-
- window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
- if (GTK_IS_BUILDABLE (object))
- id = gtk_buildable_get_name (GTK_BUILDABLE (object));
-
- set_window_title (GTK_WINDOW (window), filename, id);
-
- g_object_ref (widget);
- if (gtk_widget_get_parent (widget) != NULL)
- gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget);
- gtk_container_add (GTK_CONTAINER (window), widget);
- g_object_unref (widget);
- }
-
- gtk_window_present (GTK_WINDOW (window));
-
- gtk_main ();
-
- g_object_unref (builder);
-}
-
-static void
-do_preview (int *argc,
- const char ***argv)
-{
- GOptionContext *context;
- char *id = NULL;
- char *css = NULL;
- char **filenames = NULL;
- const GOptionEntry entries[] = {
- { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL },
- { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL },
- { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
- { NULL, }
- };
- GError *error = NULL;
-
- context = g_option_context_new (NULL);
- g_option_context_set_help_enabled (context, FALSE);
- g_option_context_add_main_entries (context, entries, NULL);
-
- if (!g_option_context_parse (context, argc, (char ***)argv, &error))
- {
- g_printerr ("%s\n", error->message);
- g_error_free (error);
- exit (1);
- }
-
- g_option_context_free (context);
-
- if (filenames == NULL)
- {
- g_printerr ("No .ui file specified\n");
- exit (1);
- }
-
- if (g_strv_length (filenames) > 1)
- {
- g_printerr ("Can only preview a single .ui file\n");
- exit (1);
- }
-
- preview_file (filenames[0], id, css);
-
- g_strfreev (filenames);
- g_free (id);
- g_free (css);
-}
-
-static void
-usage (void)
-{
- g_print (_("Usage:\n"
- " gtk-builder-tool [COMMAND] FILE\n"
- "\n"
- "Commands:\n"
- " validate Validate the file\n"
- " simplify [OPTIONS] Simplify the file\n"
- " enumerate List all named objects\n"
- " preview [OPTIONS] Preview the file\n"
- "\n"
- "Simplify Options:\n"
- " --replace Replace the file\n"
- "\n"
- "Preview Options:\n"
- " --id=ID Preview only the named object\n"
- " --css=FILE Use style from CSS file\n"
- "\n"
- "Perform various tasks on GtkBuilder .ui files.\n"));
- exit (1);
-}
-
-int
-main (int argc, const char *argv[])
-{
- g_set_prgname ("gtk-builder-tool");
-
- gtk_init ();
-
- gtk_test_register_all_types ();
-
- if (argc < 3)
- usage ();
-
- if (strcmp (argv[2], "--help") == 0)
- usage ();
-
- argv++;
- argc--;
-
- if (strcmp (argv[0], "validate") == 0)
- do_validate (argv[1]);
- else if (strcmp (argv[0], "simplify") == 0)
- do_simplify (&argc, &argv);
- else if (strcmp (argv[0], "enumerate") == 0)
- do_enumerate (argv[1]);
- else if (strcmp (argv[0], "preview") == 0)
- do_preview (&argc, &argv);
- else
- usage ();
-
- return 0;
-}
+++ /dev/null
-/* GTK - The GIMP Toolkit
- *
- * Copyright (C) 2012 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Tomas Bzatek <tbzatek@redhat.com>
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <unistd.h>
-#include <locale.h>
-#include <errno.h>
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
-#include <gio/gdesktopappinfo.h>
-#endif
-#include <gtk.h>
-
-static gboolean show_version;
-static gchar **args = NULL;
-
-static GOptionEntry entries[] = {
- { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Show program version"), NULL },
- { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, NULL },
- { NULL}
-};
-
-int
-main (int argc, char *argv[])
-{
- GError *error = NULL;
- GOptionContext *context = NULL;
- gchar *summary;
- gchar *app_name;
-#ifdef G_OS_UNIX
- gchar *desktop_file_name;
- gchar *bus_name = NULL;
-#endif
- GAppInfo *info = NULL;
- GAppLaunchContext *launch_context;
- GList *l;
- GFile *f;
-
- setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
- bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
- textdomain (GETTEXT_PACKAGE);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
- context =
- /* Translators: this message will appear immediately after the */
- /* usage string - Usage: COMMAND [OPTION…] <THIS_MESSAGE> */
- g_option_context_new (_("APPLICATION [URI…] — launch an APPLICATION"));
-
- /* Translators: this message will appear after the usage string */
- /* and before the list of options. */
- summary = _("Launch an application (specified by its desktop file name),\n"
- "optionally passing one or more URIs as arguments.");
- g_option_context_set_summary (context, summary);
- g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
- g_option_context_parse (context, &argc, &argv, &error);
-
- g_option_context_free (context);
-
- if (error != NULL)
- {
- g_printerr (_("Error parsing commandline options: %s\n"), error->message);
- g_printerr ("\n");
- g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
- g_printerr ("\n");
- g_error_free (error);
- return 1;
- }
-
- if (show_version)
- {
- g_print ("%d.%d.%d\n",
- gtk_get_major_version (),
- gtk_get_minor_version (),
- gtk_get_micro_version ());
- return 0;
- }
-
- if (!args)
- {
- /* Translators: the %s is the program name. This error message */
- /* means the user is calling gtk-launch without any argument. */
- g_printerr (_("%s: missing application name"), g_get_prgname ());
- g_printerr ("\n");
- g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
- g_printerr ("\n");
- return 1;
- }
-
-
- gtk_init ();
-
- app_name = *args;
-#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
- bus_name = g_strdup (app_name);
- if (g_str_has_suffix (app_name, ".desktop"))
- {
- desktop_file_name = g_strdup (app_name);
- bus_name[strlen (bus_name) - strlen(".desktop")] = '\0';
- }
- else
- {
- desktop_file_name = g_strconcat (app_name, ".desktop", NULL);
- }
-
- if (!g_dbus_is_name (bus_name))
- g_clear_pointer (&bus_name, g_free);
- info = G_APP_INFO (g_desktop_app_info_new (desktop_file_name));
- g_free (desktop_file_name);
-#else
-#warning Please add support for creating AppInfo from id for your OS
- g_printerr (_("Creating AppInfo from id not supported on non unix operating systems"));
-#endif
- args++;
-
- if (!info)
- {
- /* Translators: the first %s is the program name, the second one */
- /* is the application name. */
- g_printerr (_("%s: no such application %s"),
- g_get_prgname (), app_name);
- g_printerr ("\n");
- return 2;
- }
-
- l = NULL;
- for (; *args; args++)
- {
- f = g_file_new_for_commandline_arg (*args);
- l = g_list_append (l, f);
- }
-
- launch_context = (GAppLaunchContext*) gdk_display_get_app_launch_context (gdk_display_get_default ());
- if (!g_app_info_launch (info, l, launch_context, &error))
- {
- /* Translators: the first %s is the program name, the second one */
- /* is the error message. */
- g_printerr (_("%s: error launching application: %s\n"),
- g_get_prgname (), error->message);
- return 3;
- }
- g_object_unref (info);
- g_object_unref (launch_context);
-
-#ifdef G_OS_UNIX
- if (bus_name != NULL)
- {
- GDBusConnection *connection;
- gchar *object_path, *p;
-
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
-
- object_path = g_strdup_printf ("/%s", bus_name);
- for (p = object_path; *p != '\0'; p++)
- if (*p == '.')
- *p = '/';
-
- if (connection)
- g_dbus_connection_call_sync (connection,
- bus_name,
- object_path,
- "org.freedesktop.DBus.Peer",
- "Ping",
- NULL, NULL,
- G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
- g_clear_pointer (&object_path, g_free);
- g_clear_object (&connection);
- g_clear_pointer (&bus_name, g_free);
- }
-#endif
- g_list_free_full (l, g_object_unref);
-
- return 0;
-}
+++ /dev/null
-/* Copyright 2015 Timm Bäder
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING. If not,
- * see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <gtk/gtk.h>
-#include <string.h>
-
-
-int
-main (int argc, char **argv)
-{
- GtkSettings *settings;
- GParamSpec **props;
- guint n_properties;
- guint i;
- int max_prop_name_length = 0;
- gchar *pattern = NULL;
-
- gtk_init ();
-
- if (argc > 1)
- pattern = argv[1];
-
- settings = gtk_settings_get_default ();
- props = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties);
-
- for (i = 0; i < n_properties; i ++)
- {
- int len = strlen (props[i]->name);
-
- if (len > max_prop_name_length)
- max_prop_name_length = len;
- }
-
-
- for (i = 0; i < n_properties; i ++)
- {
- GValue value = {0};
- GParamSpec *prop = props[i];
- gchar *value_str;
- int spacing = max_prop_name_length - strlen (prop->name) + 1;
- gboolean deprecated;
-
- if (pattern && !g_strrstr (prop->name, pattern))
- continue;
-
- g_value_init (&value, prop->value_type);
- g_object_get_property (G_OBJECT (settings), prop->name, &value);
- deprecated = prop->flags & G_PARAM_DEPRECATED;
-
- if (G_VALUE_HOLDS_ENUM (&value))
- {
- GEnumClass *enum_class = G_PARAM_SPEC_ENUM (prop)->enum_class;
- GEnumValue *enum_value = g_enum_get_value (enum_class, g_value_get_enum (&value));
-
- value_str = g_strdup (enum_value->value_name);
- }
- else
- {
- value_str = g_strdup_value_contents (&value);
- }
-
- if (deprecated)
- {
- printf ("!");
- spacing --;
- }
-
- for (; spacing >= 0; spacing --)
- printf (" ");
-
- printf ("%s: %s\n", prop->name, value_str);
-
- g_free (value_str);
- g_value_unset (&value);
- }
-
- g_free (props);
-
- return 0;
-}
+++ /dev/null
-<?xml version="1.0"?>
-<its:rules xmlns:its="http://www.w3.org/2005/11/its"
- xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
- version="2.0">
- <its:translateRule selector="/interface" translate="no"/>
- <its:translateRule selector="/interface//*[@translatable = 'yes']"
- translate="yes"/>
-
- <!-- The 'comment' attribute should be extracted as a translator comment. -->
- <its:locNoteRule selector="/interface//*[@comments]"
- locNotePointer="@comments"
- locNoteType="alert"/>
- <gt:escapeRule selector="/interface//@comments" escape="no"/>
-
- <!-- The 'context' attribute should be extracted as msgctxt. -->
- <gt:contextRule selector="/interface//*[@context]" contextPointer="@context"/>
-
- <its:preserveSpaceRule selector="/interface" space="preserve"/>
-
- <!-- Extracted strings are consumed by the library and are never
- merged back; we don't want to escape special characters. -->
- <gt:escapeRule selector="/interface" escape="no"/>
-</its:rules>
+++ /dev/null
-<?xml version="1.0"?>
-<locatingRules>
- <locatingRule name="GtkBuilder" pattern="*.ui">
- <documentRule localName="interface" target="gtkbuilder.its"/>
- </locatingRule>
-</locatingRules>
+++ /dev/null
-<?xml version="1.0"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="">
- <start>
- <element name="interface">
- <optional>
- <attribute name="domain">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="requires"/>
- <ref name="object"/>
- <ref name="template"/>
- <ref name="menu"/>
- </choice>
- </zeroOrMore>
- </element>
- </start>
- <define name="requires">
- <element name="requires">
- <attribute name="lib">
- <text/>
- </attribute>
- <attribute name="version">
- <text/>
- </attribute>
- </element>
- </define>
- <define name="object">
- <element name="object">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <attribute name="class">
- <text/>
- </attribute>
- <optional>
- <attribute name="type-func">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="constructor">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="property"/>
- <ref name="signal"/>
- <ref name="child"/>
- <ref name="ANY"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="template">
- <element name="template">
- <attribute name="class">
- <text/>
- </attribute>
- <attribute name="parent">
- <text/>
- </attribute>
- <zeroOrMore>
- <choice>
- <ref name="property"/>
- <ref name="signal"/>
- <ref name="child"/>
- <ref name="ANY"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="property">
- <element name="property">
- <attribute name="name">
- <text/>
- </attribute>
- <optional>
- <attribute name="translatable">
- <choice>
- <value>yes</value>
- <value>no</value>
- </choice>
- </attribute>
- </optional>
- <optional>
- <attribute name="comments">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="context">
- <text/>
- </attribute>
- </optional>
- <optional>
- <group>
- <attribute name="bind-source">
- <text/>
- </attribute>
- <attribute name="bind-property">
- <text/>
- </attribute>
- <optional>
- <attribute name="bind-flags">
- <text/>
- </attribute>
- </optional>
- </group>
- </optional>
- <optional>
- <text/>
- </optional>
- </element>
- </define>
- <define name="signal">
- <element name="signal">
- <attribute name="name">
- <text/>
- </attribute>
- <attribute name="handler">
- <text/>
- </attribute>
- <optional>
- <attribute name="after">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="swapped">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="object">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="last_modification_time">
- <text/>
- </attribute>
- </optional>
- <empty/>
- </element>
- </define>
- <define name="child">
- <element name="child">
- <optional>
- <attribute name="type">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="internal-child">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="object"/>
- <ref name="ANY"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="menu">
- <element name="menu">
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- <optional>
- <attribute name="domain">
- <text/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="item"/>
- <ref name="submenu"/>
- <ref name="section"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="item">
- <element name="item">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="attribute_"/>
- <ref name="link"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="attribute_">
- <element name="attribute">
- <attribute name="name">
- <text/>
- </attribute>
- <optional>
- <attribute name="type">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="translatable">
- <choice>
- <value>yes</value>
- <value>no</value>
- </choice>
- </attribute>
- </optional>
- <optional>
- <attribute name="context">
- <text/>
- </attribute>
- </optional>
- <optional>
- <attribute name="comments">
- <text/>
- </attribute>
- </optional>
- <optional>
- <text/>
- </optional>
- </element>
- </define>
- <define name="link">
- <element name="link">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <attribute name="name">
- <text/>
- </attribute>
- <zeroOrMore>
- <ref name="item"/>
- </zeroOrMore>
- </element>
- </define>
- <define name="submenu">
- <element name="submenu">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="attribute_"/>
- <ref name="item"/>
- <ref name="submenu"/>
- <ref name="section"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="section">
- <element name="section">
- <optional>
- <attribute name="id">
- <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
- </attribute>
- </optional>
- <zeroOrMore>
- <choice>
- <ref name="attribute_"/>
- <ref name="item"/>
- <ref name="submenu"/>
- <ref name="section"/>
- </choice>
- </zeroOrMore>
- </element>
- </define>
- <define name="ANY">
- <element>
- <anyName>
- <except>
- <name>interface</name>
- <name>requires</name>
- <name>object</name>
- <name>property</name>
- <name>signal</name>
- <name>child</name>
- <name>menu</name>
- <name>item</name>
- <name>attribute</name>
- <name>link</name>
- <name>submenu</name>
- <name>section</name>
- </except>
- </anyName>
- <zeroOrMore>
- <attribute>
- <anyName/>
- <text/>
- </attribute>
- </zeroOrMore>
- <interleave>
- <zeroOrMore>
- <ref name="ALL"/>
- </zeroOrMore>
- <optional>
- <text/>
- </optional>
- </interleave>
- </element>
- </define>
- <define name="ALL">
- <element>
- <anyName/>
- <zeroOrMore>
- <attribute>
- <anyName/>
- <text/>
- </attribute>
- </zeroOrMore>
- <interleave>
- <zeroOrMore>
- <ref name="ALL"/>
- </zeroOrMore>
- <optional>
- <text/>
- </optional>
- </interleave>
- </element>
- </define>
-</grammar>
+++ /dev/null
-/* gtkiconcachevalidator.c
- * Copyright (C) 2007 Red Hat, Inc
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-#include "config.h"
-#include "gtkiconcachevalidatorprivate.h"
-
-#include <glib.h>
-#include <gdk-pixbuf/gdk-pixdata.h>
-
-
-#define VERBOSE(x)
-
-#define check(name,condition) \
- if (!(condition)) \
- { \
- VERBOSE(g_message ("bad %s", (name))); \
- return FALSE; \
- }
-
-static inline gboolean
-get_uint16 (CacheInfo *info,
- guint32 offset,
- guint16 *value)
-{
- if (offset < info->cache_size)
- {
- *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
- return TRUE;
- }
- else
- {
- *value = 0;
- return FALSE;
- }
-}
-
-static inline gboolean
-get_uint32 (CacheInfo *info,
- guint32 offset,
- guint32 *value)
-{
- if (offset < info->cache_size)
- {
- *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
- return TRUE;
- }
- else
- {
- *value = 0;
- return FALSE;
- }
-}
-
-static gboolean
-check_version (CacheInfo *info)
-{
- guint16 major, minor;
-
- check ("major version", get_uint16 (info, 0, &major) && major == 1);
- check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
-
- return TRUE;
-}
-
-static gboolean
-check_string (CacheInfo *info,
- guint32 offset)
-{
- check ("string offset", offset < info->cache_size);
-
- if (info->flags & CHECK_STRINGS)
- {
- gint i;
- gchar c;
-
- /* assume no string is longer than 1k */
- for (i = 0; i < 1024; i++)
- {
- check ("string offset", offset + i < info->cache_size)
- c = *(info->cache + offset + i);
- if (c == '\0')
- break;
- check ("string content", g_ascii_isgraph (c));
- }
- check ("string length", i < 1024);
- }
-
- return TRUE;
-}
-
-static gboolean
-check_string_utf8 (CacheInfo *info,
- guint32 offset)
-{
- check ("string offset", offset < info->cache_size);
-
- if (info->flags & CHECK_STRINGS)
- {
- gint i;
- gchar c;
-
- /* assume no string is longer than 1k */
- for (i = 0; i < 1024; i++)
- {
- check ("string offset", offset + i < info->cache_size)
- c = *(info->cache + offset + i);
- if (c == '\0')
- break;
- }
- check ("string length", i < 1024);
- check ("string utf8 data", g_utf8_validate((char *)(info->cache + offset), -1, NULL));
- }
-
- return TRUE;
-}
-
-static gboolean
-check_directory_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 directory_offset;
- gint i;
-
- check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
-
- for (i = 0; i < info->n_directories; i++)
- {
- check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
- if (!check_string (info, directory_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_pixel_data (CacheInfo *info,
- guint32 offset)
-{
- guint32 type;
- guint32 length;
-
- check ("offset, pixel data type", get_uint32 (info, offset, &type));
- check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
-
- check ("pixel data type", type == 0);
- check ("pixel data length", offset + 8 + length < info->cache_size);
-
- if (info->flags & CHECK_PIXBUFS)
- {
- GdkPixdata data;
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
- check ("pixel data", gdk_pixdata_deserialize (&data, length,
- (const guint8*)info->cache + offset + 8,
- NULL));
-G_GNUC_END_IGNORE_DEPRECATIONS;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_embedded_rect (CacheInfo *info,
- guint32 offset)
-{
- check ("embedded rect", offset + 4 < info->cache_size);
-
- return TRUE;
-}
-
-static gboolean
-check_attach_point_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_attach_points;
-
- check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
- check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
-
- return TRUE;
-}
-
-static gboolean
-check_display_name_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_display_names, ofs;
- gint i;
-
- check ("offset, display name list",
- get_uint32 (info, offset, &n_display_names));
- for (i = 0; i < n_display_names; i++)
- {
- get_uint32(info, offset + 4 + 8 * i, &ofs);
- if (!check_string (info, ofs))
- return FALSE;
- get_uint32(info, offset + 4 + 8 * i + 4, &ofs);
- if (!check_string_utf8 (info, ofs))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_meta_data (CacheInfo *info,
- guint32 offset)
-{
- guint32 embedded_rect_offset;
- guint32 attach_point_list_offset;
- guint32 display_name_list_offset;
-
- check ("offset, embedded rect",
- get_uint32 (info, offset, &embedded_rect_offset));
- check ("offset, attach point list",
- get_uint32 (info, offset + 4, &attach_point_list_offset));
- check ("offset, display name list",
- get_uint32 (info, offset + 8, &display_name_list_offset));
-
- if (embedded_rect_offset != 0)
- {
- if (!check_embedded_rect (info, embedded_rect_offset))
- return FALSE;
- }
-
- if (attach_point_list_offset != 0)
- {
- if (!check_attach_point_list (info, attach_point_list_offset))
- return FALSE;
- }
-
- if (display_name_list_offset != 0)
- {
- if (!check_display_name_list (info, display_name_list_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_image_data (CacheInfo *info,
- guint32 offset)
-{
- guint32 pixel_data_offset;
- guint32 meta_data_offset;
-
- check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
- check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
-
- if (pixel_data_offset != 0)
- {
- if (!check_pixel_data (info, pixel_data_offset))
- return FALSE;
- }
- if (meta_data_offset != 0)
- {
- if (!check_meta_data (info, meta_data_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_image (CacheInfo *info,
- guint32 offset)
-{
- guint16 index;
- guint16 flags;
- guint32 image_data_offset;
-
- check ("offset, image index", get_uint16 (info, offset, &index));
- check ("offset, image flags", get_uint16 (info, offset + 2, &flags));
- check ("offset, image data offset",
- get_uint32 (info, offset + 4, &image_data_offset));
-
- check ("image index", index < info->n_directories);
- check ("image flags", flags < 16);
-
- if (image_data_offset != 0)
- {
- if (!check_image_data (info, image_data_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_image_list (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_images;
- gint i;
-
- check ("offset, image list", get_uint32 (info, offset, &n_images));
-
- for (i = 0; i < n_images; i++)
- {
- if (!check_image (info, offset + 4 + 8 * i))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_icon (CacheInfo *info,
- guint32 offset)
-{
- guint32 chain_offset;
- guint32 name_offset;
- guint32 image_list_offset;
-
- check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
- check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
- check ("offset, icon image list", get_uint32 (info, offset + 8,
- &image_list_offset));
-
- if (!check_string (info, name_offset))
- return FALSE;
- if (!check_image_list (info, image_list_offset))
- return FALSE;
- if (chain_offset != 0xffffffff)
- {
- if (!check_icon (info, chain_offset))
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_hash (CacheInfo *info,
- guint32 offset)
-{
- guint32 n_buckets, icon_offset;
- gint i;
-
- check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
-
- for (i = 0; i < n_buckets; i++)
- {
- check ("offset, hash chain",
- get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
- if (icon_offset != 0xffffffff)
- {
- if (!check_icon (info, icon_offset))
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/**
- * gtk_icon_cache_validate:
- * @info: a CacheInfo structure
- *
- * Validates the icon cache passed in the @cache and
- * @cache_size fields of the @info structure. The
- * validator checks that offsets specified in the
- * cache do not point outside the mapped area, that
- * strings look reasonable, and that pixbufs can
- * be deserialized. The amount of validation can
- * be controlled with the @flags field.
- *
- * Returns: %TRUE if the cache is valid
- */
-gboolean
-gtk_icon_cache_validate (CacheInfo *info)
-{
- guint32 hash_offset;
- guint32 directory_list_offset;
-
- if (!check_version (info))
- return FALSE;
- check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
- check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
- if (!check_directory_list (info, directory_list_offset))
- return FALSE;
-
- if (!check_hash (info, hash_offset))
- return FALSE;
-
- return TRUE;
-}
-
gtk_private_sources = files([
'fallback-c89.c',
'fnmatch.c',
- 'gdkpixbufutils.c',
+ 'tools/gdkpixbufutils.c',
'gtkactionhelper.c',
'gtkactionmuxer.c',
'gtkactionobservable.c',
'gtkhsla.c',
'gtkicon.c',
'gtkiconcache.c',
- 'gtkiconcachevalidator.c',
+ 'tools/gtkiconcachevalidator.c',
'gtkiconhelper.c',
'gtkkineticscrolling.c',
'gtkkeyhash.c',
link_with: libgtk,
link_args: common_ldflags)
-# Installed tools
-gtk_tools = [
- ['gtk4-query-settings', ['gtk-query-settings.c']],
- ['gtk4-builder-tool', ['gtk-builder-tool.c']],
- ['gtk4-update-icon-cache', ['updateiconcache.c', 'gtkiconcachevalidator.c']],
- ['gtk4-encode-symbolic-svg', ['encodesymbolic.c', 'gdkpixbufutils.c']],
-]
-
-if os_unix
- gtk_tools += [['gtk4-launch', ['gtk-launch.c']]]
-endif
-
-foreach tool: gtk_tools
- tool_name = tool.get(0)
- tool_srcs = tool.get(1)
-
- exe = executable(tool_name, tool_srcs,
- include_directories: [confinc],
- c_args: gtk_cargs,
- dependencies: libgtk_dep,
- install: true)
-
- set_variable(tool_name.underscorify(), exe) # used in testsuites
-endforeach
-
-# Data to install
-install_data('gtkbuilder.rng',
- install_dir: join_paths(gtk_datadir, 'gtk-4.0'))
-
-install_data('gtkbuilder.loc', 'gtkbuilder.its',
- install_dir: join_paths(gtk_datadir, 'gettext/its'))
-
if quartz_enabled
# HACK: install_data() doesn't allow installing under a different name (#1487)
# FIXME: or maybe just rename it to gtk-keys.css in the src tree?
configuration: configuration_data(),
install_dir: join_paths(get_option('datadir'), 'themes/Mac/gtk-4.0'))
endif
+
+subdir('tools')
--- /dev/null
+/* encodesymbolic.c
+ * Copyright (C) 2014 Alexander Larsson <alexl@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "gdkpixbufutilsprivate.h"
+
+static gchar *output_dir = NULL;
+
+static GOptionEntry args[] = {
+ { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_dir, N_("Output to this directory instead of cwd"), NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ gchar *path, *basename, *pngpath, *pngfile, *dot;
+ GOptionContext *context;
+ GdkPixbuf *symbolic;
+ GError *error;
+ int width, height;
+ gchar **sizev;
+ GFileOutputStream *out;
+ GFile *dest;
+ char *data;
+ gsize len;
+
+ setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+ g_set_prgname ("gtk-encode-symbolic-svg");
+
+ context = g_option_context_new ("PATH WIDTHxHEIGHT");
+ g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
+
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ if (argc < 3)
+ {
+ g_printerr ("%s\n", g_option_context_get_help (context, FALSE, NULL));
+ return 1;
+ }
+
+ width = 0;
+ height = 0;
+ sizev = g_strsplit (argv[2], "x", 0);
+ if (g_strv_length (sizev) == 2)
+ {
+ width = atoi(sizev[0]);
+ height = atoi(sizev[1]);
+ }
+ g_strfreev (sizev);
+
+ if (width == 0 || height == 0)
+ {
+ g_printerr (_("Invalid size %s\n"), argv[2]);
+ return 1;
+ }
+
+ path = argv[1];
+#ifdef G_OS_WIN32
+ path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
+#endif
+
+ error = NULL;
+ if (!g_file_get_contents (path, &data, &len, &error))
+ {
+ g_printerr (_("Can’t load file: %s\n"), error->message);
+ return 1;
+ }
+
+ symbolic = gtk_make_symbolic_pixbuf_from_data (data, len, width, height, 1.0, &error);
+ if (symbolic == NULL)
+ {
+ g_printerr (_("Can’t load file: %s\n"), error->message);
+ return 1;
+ }
+
+ g_free (data);
+
+ basename = g_path_get_basename (path);
+
+ dot = strrchr (basename, '.');
+ if (dot != NULL)
+ *dot = 0;
+ pngfile = g_strconcat (basename, ".symbolic.png", NULL);
+ g_free (basename);
+
+ if (output_dir != NULL)
+ pngpath = g_build_filename (output_dir, pngfile, NULL);
+ else
+ pngpath = g_strdup (pngfile);
+
+ g_free (pngfile);
+
+ dest = g_file_new_for_path (pngpath);
+
+
+ out = g_file_replace (dest,
+ NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ NULL, &error);
+ if (out == NULL)
+ {
+ g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
+ return 1;
+ }
+
+ if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL))
+ {
+ g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
+ return 1;
+ }
+
+ if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, &error))
+ {
+ g_printerr (_("Can’t close stream"));
+ return 1;
+ }
+
+ g_object_unref (out);
+ g_free (pngpath);
+
+ return 0;
+}
--- /dev/null
+/* Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk/gdk.h>
+#include "gdkpixbufutilsprivate.h"
+
+static GdkPixbuf *
+load_from_stream (GdkPixbufLoader *loader,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ gssize n_read;
+ guchar buffer[65536];
+ gboolean res;
+
+ res = TRUE;
+ while (1)
+ {
+ n_read = g_input_stream_read (stream, buffer, sizeof (buffer), cancellable, error);
+ if (n_read < 0)
+ {
+ res = FALSE;
+ error = NULL; /* Ignore further errors */
+ break;
+ }
+
+ if (n_read == 0)
+ break;
+
+ if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error))
+ {
+ res = FALSE;
+ error = NULL;
+ break;
+ }
+ }
+
+ if (!gdk_pixbuf_loader_close (loader, error))
+ {
+ res = FALSE;
+ error = NULL;
+ }
+
+ pixbuf = NULL;
+
+ if (res)
+ {
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (pixbuf)
+ g_object_ref (pixbuf);
+ }
+
+ return pixbuf;
+}
+
+static void
+size_prepared_cb (GdkPixbufLoader *loader,
+ gint width,
+ gint height,
+ gpointer data)
+{
+ gdouble *scale = data;
+
+ width = MAX (*scale * width, 1);
+ height = MAX (*scale * height, 1);
+
+ gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+/* Like gdk_pixbuf_new_from_stream_at_scale, but
+ * load the image at its original size times the
+ * given scale.
+ */
+GdkPixbuf *
+_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
+ gdouble scale,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GdkPixbufLoader *loader;
+ GdkPixbuf *pixbuf;
+
+ loader = gdk_pixbuf_loader_new ();
+
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (size_prepared_cb), &scale);
+
+ pixbuf = load_from_stream (loader, stream, cancellable, error);
+
+ g_object_unref (loader);
+
+ return pixbuf;
+}
+
+/* Like gdk_pixbuf_new_from_resource_at_scale, but
+ * load the image at its original size times the
+ * given scale.
+ */
+GdkPixbuf *
+_gdk_pixbuf_new_from_resource_scaled (const gchar *resource_path,
+ gdouble scale,
+ GError **error)
+{
+ GInputStream *stream;
+ GdkPixbuf *pixbuf;
+
+ stream = g_resources_open_stream (resource_path, 0, error);
+ if (stream == NULL)
+ return NULL;
+
+ pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, scale, NULL, error);
+ g_object_unref (stream);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+load_symbolic_svg (const char *file_data,
+ gsize file_len,
+ int width,
+ int height,
+ double scale,
+ const GdkRGBA *fg,
+ const GdkRGBA *success_color,
+ const GdkRGBA *warning_color,
+ const GdkRGBA *error_color,
+ GError **error)
+{
+ GInputStream *stream;
+ GdkPixbuf *pixbuf;
+ gchar *css_fg;
+ gchar *css_success;
+ gchar *css_warning;
+ gchar *css_error;
+ gchar *data;
+ gchar *svg_width, *svg_height;
+ gchar *escaped_file_data;
+
+ css_fg = gdk_rgba_to_string (fg);
+
+ css_success = css_warning = css_error = NULL;
+
+ css_warning = gdk_rgba_to_string (warning_color);
+ css_error = gdk_rgba_to_string (error_color);
+ css_success = gdk_rgba_to_string (success_color);
+
+ /* Fetch size from the original icon */
+ stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
+ pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
+ g_object_unref (stream);
+
+ if (!pixbuf)
+ return NULL;
+
+ if (width == 0)
+ width = gdk_pixbuf_get_width (pixbuf) * scale;
+ if (height == 0)
+ height = gdk_pixbuf_get_height (pixbuf) * scale;
+
+ svg_width = g_strdup_printf ("%d", gdk_pixbuf_get_width (pixbuf));
+ svg_height = g_strdup_printf ("%d", gdk_pixbuf_get_height (pixbuf));
+ g_object_unref (pixbuf);
+
+ escaped_file_data = g_markup_escape_text (file_data, file_len);
+
+ data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+ "<svg version=\"1.1\"\n"
+ " xmlns=\"http://www.w3.org/2000/svg\"\n"
+ " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
+ " width=\"", svg_width, "\"\n"
+ " height=\"", svg_height, "\">\n"
+ " <style type=\"text/css\">\n"
+ " rect,circle,path {\n"
+ " fill: ", css_fg," !important;\n"
+ " }\n"
+ " .warning {\n"
+ " fill: ", css_warning, " !important;\n"
+ " }\n"
+ " .error {\n"
+ " fill: ", css_error ," !important;\n"
+ " }\n"
+ " .success {\n"
+ " fill: ", css_success, " !important;\n"
+ " }\n"
+ " </style>\n"
+ " <xi:include href=\"data:text/xml,", escaped_file_data, "\"/>\n"
+ "</svg>",
+ NULL);
+ g_free (escaped_file_data);
+ g_free (css_fg);
+ g_free (css_warning);
+ g_free (css_error);
+ g_free (css_success);
+ g_free (svg_width);
+ g_free (svg_height);
+
+ stream = g_memory_input_stream_new_from_data (data, -1, g_free);
+ pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error);
+ g_object_unref (stream);
+
+ return pixbuf;
+}
+
+static void
+extract_plane (GdkPixbuf *src,
+ GdkPixbuf *dst,
+ int from_plane,
+ int to_plane)
+{
+ guchar *src_data, *dst_data;
+ int width, height, src_stride, dst_stride;
+ guchar *src_row, *dst_row;
+ int x, y;
+
+ width = gdk_pixbuf_get_width (src);
+ height = gdk_pixbuf_get_height (src);
+
+ g_assert (width <= gdk_pixbuf_get_width (dst));
+ g_assert (height <= gdk_pixbuf_get_height (dst));
+
+ src_stride = gdk_pixbuf_get_rowstride (src);
+ src_data = gdk_pixbuf_get_pixels (src);
+
+ dst_data = gdk_pixbuf_get_pixels (dst);
+ dst_stride = gdk_pixbuf_get_rowstride (dst);
+
+ for (y = 0; y < height; y++)
+ {
+ src_row = src_data + src_stride * y;
+ dst_row = dst_data + dst_stride * y;
+ for (x = 0; x < width; x++)
+ {
+ dst_row[to_plane] = src_row[from_plane];
+ src_row += 4;
+ dst_row += 4;
+ }
+ }
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_data (const char *file_data,
+ gsize file_len,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+
+{
+ GdkRGBA r = { 1,0,0,1}, g = {0,1,0,1};
+ GdkPixbuf *loaded;
+ GdkPixbuf *pixbuf = NULL;
+ int plane;
+
+ for (plane = 0; plane < 3; plane++)
+ {
+ /* Here we render the svg with all colors solid, this should
+ * always make the alpha channel the same and it should match
+ * the final alpha channel for all possible renderings. We
+ * Just use it as-is for final alpha.
+ *
+ * For the 3 non-fg colors, we render once each with that
+ * color as red, and every other color as green. The resulting
+ * red will describe the amount of that color is in the
+ * opaque part of the color. We store these as the rgb
+ * channels, with the color of the fg being implicitly
+ * the "rest", as all color fractions should add up to 1.
+ */
+ loaded = load_symbolic_svg (file_data, file_len, width, height, scale,
+ &g,
+ plane == 0 ? &r : &g,
+ plane == 1 ? &r : &g,
+ plane == 2 ? &r : &g,
+ error);
+ if (loaded == NULL)
+ return NULL;
+
+ if (pixbuf == NULL)
+ {
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ gdk_pixbuf_get_width (loaded),
+ gdk_pixbuf_get_height (loaded));
+ gdk_pixbuf_fill (pixbuf, 0);
+ }
+
+ if (plane == 0)
+ extract_plane (loaded, pixbuf, 3, 3);
+
+ extract_plane (loaded, pixbuf, 0, plane);
+
+ g_object_unref (loaded);
+ }
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_resource (const char *path,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ GBytes *bytes;
+ const char *data;
+ gsize size;
+ GdkPixbuf *pixbuf;
+
+ bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
+ if (bytes == NULL)
+ return NULL;
+
+ data = g_bytes_get_data (bytes, &size);
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, error);
+
+ g_bytes_unref (bytes);
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_file (GFile *file,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ char *data;
+ gsize size;
+ GdkPixbuf *pixbuf;
+
+ if (!g_file_load_contents (file, NULL, &data, &size, NULL, error))
+ return NULL;
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, error);
+
+ g_free (data);
+
+ return pixbuf;
+}
+
+GdkTexture *
+gtk_make_symbolic_texture_from_resource (const char *path,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ GdkTexture *texture;
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_resource (path, width, height, scale, error);
+ texture = gdk_texture_new_for_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return texture;
+}
+
+GdkTexture *
+gtk_make_symbolic_texture_from_file (GFile *file,
+ int width,
+ int height,
+ double scale,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ GdkTexture *texture;
+
+ pixbuf = gtk_make_symbolic_pixbuf_from_file (file, width, height, scale, error);
+ texture = gdk_texture_new_for_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return texture;
+}
--- /dev/null
+/* Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+
+
+typedef struct {
+ GtkBuilder *builder;
+ GList *classes;
+ gboolean packing;
+ gboolean packing_started;
+ gboolean cell_packing;
+ gboolean cell_packing_started;
+ gint in_child;
+ gint child_started;
+ gchar **attribute_names;
+ gchar **attribute_values;
+ GString *value;
+ gboolean unclosed_starttag;
+ gint indent;
+ char *input_filename;
+ char *output_filename;
+ FILE *output;
+} MyParserData;
+
+static void
+canonicalize_key (gchar *key)
+{
+ gchar *p;
+
+ for (p = key; *p != 0; p++)
+ {
+ gchar c = *p;
+
+ /* We may meet something like AtkObject::accessible-name */
+ if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':'))
+ continue;
+
+ if (c != '-' &&
+ (c < '0' || c > '9') &&
+ (c < 'A' || c > 'Z') &&
+ (c < 'a' || c > 'z'))
+ *p = '-';
+ }
+}
+
+static GParamSpec *
+get_property_pspec (MyParserData *data,
+ const gchar *class_name,
+ const gchar *property_name)
+{
+ GType type;
+ GObjectClass *class;
+ GParamSpec *pspec;
+ gchar *canonical_name;
+
+ type = g_type_from_name (class_name);
+ if (type == G_TYPE_INVALID)
+ {
+ GtkBuilder *builder = gtk_builder_new ();
+ type = gtk_builder_get_type_from_name (builder, class_name);
+ g_object_unref (builder);
+ if (type == G_TYPE_INVALID)
+ return NULL;
+ }
+
+ class = g_type_class_ref (type);
+ canonical_name = g_strdup (property_name);
+ canonicalize_key (canonical_name);
+ if (data->packing)
+ pspec = gtk_container_class_find_child_property (class, canonical_name);
+ else if (data->cell_packing)
+ {
+ GObjectClass *cell_class;
+
+ /* We're just assuming that the cell layout is using a GtkCellAreaBox. */
+ cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX);
+ pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name);
+ g_type_class_unref (cell_class);
+ }
+ else
+ pspec = g_object_class_find_property (class, canonical_name);
+ g_free (canonical_name);
+ g_type_class_unref (class);
+
+ return pspec;
+}
+
+
+static gboolean
+value_is_default (MyParserData *data,
+ const gchar *class_name,
+ const gchar *property_name,
+ const gchar *value_string)
+{
+ GValue value = { 0, };
+ gboolean ret;
+ GError *error = NULL;
+ GParamSpec *pspec;
+
+ pspec = get_property_pspec (data, class_name, property_name);
+
+ if (pspec == NULL)
+ {
+ if (data->packing)
+ g_printerr (_("Packing property %s::%s not found\n"), class_name, property_name);
+ else if (data->cell_packing)
+ g_printerr (_("Cell property %s::%s not found\n"), class_name, property_name);
+ else
+ g_printerr (_("Property %s::%s not found\n"), class_name, property_name);
+ return FALSE;
+ }
+ else if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
+ return FALSE;
+
+ if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error))
+ {
+ g_printerr (_("Couldn’t parse value for %s::%s: %s\n"), class_name, property_name, error->message);
+ g_error_free (error);
+ ret = FALSE;
+ }
+ else
+ ret = g_param_value_defaults (pspec, &value);
+
+ g_value_reset (&value);
+
+ return ret;
+}
+
+static gboolean
+property_is_boolean (MyParserData *data,
+ const gchar *class_name,
+ const gchar *property_name)
+{
+ GParamSpec *pspec;
+
+ pspec = get_property_pspec (data, class_name, property_name);
+ if (pspec)
+ return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN;
+
+ return FALSE;
+}
+
+static const gchar *
+canonical_boolean_value (MyParserData *data,
+ const gchar *string)
+{
+ GValue value = G_VALUE_INIT;
+ gboolean b = FALSE;
+
+ if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL))
+ b = g_value_get_boolean (&value);
+
+ return b ? "1" : "0";
+}
+
+/* A number of properties unfortunately can't be omitted even
+ * if they are nominally set to their default value. In many
+ * cases, this is due to subclasses not overriding the default
+ * value from the superclass.
+ */
+static gboolean
+needs_explicit_setting (MyParserData *data,
+ const gchar *class_name,
+ const gchar *property_name)
+{
+ struct _Prop {
+ const char *class;
+ const char *property;
+ gboolean packing;
+ } props[] = {
+ { "GtkAboutDialog", "program-name", 0 },
+ { "GtkCalendar", "year", 0 },
+ { "GtkCalendar", "month", 0 },
+ { "GtkCalendar", "day", 0 },
+ { "GtkPlacesSidebar", "show-desktop", 0 },
+ { "GtkRadioButton", "draw-indicator", 0 },
+ { "GtkGrid", "left-attach", 1 },
+ { "GtkGrid", "top-attach", 1 },
+ { "GtkWidget", "hexpand", 0 },
+ { "GtkWidget", "vexpand", 0 },
+ { NULL, NULL, 0 }
+ };
+ gchar *canonical_name;
+ gboolean found;
+ gint k;
+
+ canonical_name = g_strdup (property_name);
+ g_strdelimit (canonical_name, "_", '-');
+
+ found = FALSE;
+ for (k = 0; props[k].class; k++)
+ {
+ if (strcmp (class_name, props[k].class) == 0 &&
+ strcmp (canonical_name, props[k].property) == 0 &&
+ data->packing == props[k].packing)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ g_free (canonical_name);
+
+ return found;
+}
+
+static void
+maybe_start_packing (MyParserData *data)
+{
+ if (data->packing)
+ {
+ if (!data->packing_started)
+ {
+ g_fprintf (data->output, "%*s<packing>\n", data->indent, "");
+ data->indent += 2;
+ data->packing_started = TRUE;
+ }
+ }
+}
+
+static void
+maybe_start_cell_packing (MyParserData *data)
+{
+ if (data->cell_packing)
+ {
+ if (!data->cell_packing_started)
+ {
+ g_fprintf (data->output, "%*s<cell-packing>\n", data->indent, "");
+ data->indent += 2;
+ data->cell_packing_started = TRUE;
+ }
+ }
+}
+
+static void
+maybe_start_child (MyParserData *data)
+{
+ if (data->in_child > 0)
+ {
+ if (data->child_started < data->in_child)
+ {
+ g_fprintf (data->output, "%*s<child>\n", data->indent, "");
+ data->indent += 2;
+ data->child_started += 1;
+ }
+ }
+}
+
+static void
+maybe_emit_property (MyParserData *data)
+{
+ gint i;
+ gboolean bound;
+ gboolean translatable;
+ gchar *escaped;
+ const gchar *class_name;
+ const gchar *property_name;
+ const gchar *value_string;
+
+ class_name = (const gchar *)data->classes->data;
+ property_name = "";
+ value_string = (const gchar *)data->value->str;
+
+ bound = FALSE;
+ translatable = FALSE;
+ for (i = 0; data->attribute_names[i]; i++)
+ {
+ if (strcmp (data->attribute_names[i], "bind-source") == 0 ||
+ strcmp (data->attribute_names[i], "bind_source") == 0)
+ bound = TRUE;
+ else if (strcmp (data->attribute_names[i], "translatable") == 0)
+ translatable = TRUE;
+ else if (strcmp (data->attribute_names[i], "name") == 0)
+ property_name = (const gchar *)data->attribute_values[i];
+ }
+
+ if (!translatable &&
+ !bound &&
+ !needs_explicit_setting (data, class_name, property_name))
+ {
+ for (i = 0; data->attribute_names[i]; i++)
+ {
+ if (strcmp (data->attribute_names[i], "name") == 0)
+ {
+ if (data->classes == NULL)
+ break;
+
+ if (value_is_default (data, class_name, property_name, value_string))
+ return;
+ }
+ }
+ }
+
+ maybe_start_packing (data);
+ maybe_start_cell_packing (data);
+
+ g_fprintf (data->output, "%*s<property", data->indent, "");
+ for (i = 0; data->attribute_names[i]; i++)
+ {
+ if (!translatable &&
+ (strcmp (data->attribute_names[i], "comments") == 0 ||
+ strcmp (data->attribute_names[i], "context") == 0))
+ continue;
+
+ escaped = g_markup_escape_text (data->attribute_values[i], -1);
+
+ if (strcmp (data->attribute_names[i], "name") == 0)
+ canonicalize_key (escaped);
+
+ g_fprintf (data->output, " %s=\"%s\"", data->attribute_names[i], escaped);
+ g_free (escaped);
+ }
+
+ if (bound)
+ {
+ g_fprintf (data->output, "/>\n");
+ }
+ else
+ {
+ g_fprintf (data->output, ">");
+ if (property_is_boolean (data, class_name, property_name))
+ {
+ g_fprintf (data->output, "%s", canonical_boolean_value (data, value_string));
+ }
+ else
+ {
+ escaped = g_markup_escape_text (value_string, -1);
+ g_fprintf (data->output, "%s", escaped);
+ g_free (escaped);
+ }
+ g_fprintf (data->output, "</property>\n");
+ }
+}
+
+static void
+maybe_close_starttag (MyParserData *data)
+{
+ if (data->unclosed_starttag)
+ {
+ g_fprintf (data->output, ">\n");
+ data->unclosed_starttag = FALSE;
+ }
+}
+
+static gboolean
+stack_is (GMarkupParseContext *context,
+ ...)
+{
+ va_list args;
+ gchar *s, *p;
+ const GSList *stack;
+
+ stack = g_markup_parse_context_get_element_stack (context);
+
+ va_start (args, context);
+ s = va_arg (args, gchar *);
+ while (s)
+ {
+ if (stack == NULL)
+ {
+ va_end (args);
+ return FALSE;
+ }
+
+ p = (gchar *)stack->data;
+ if (strcmp (s, p) != 0)
+ {
+ va_end (args);
+ return FALSE;
+ }
+
+ s = va_arg (args, gchar *);
+ stack = stack->next;
+ }
+
+ va_end (args);
+ return TRUE;
+}
+
+static void
+start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ gint i;
+ gchar *escaped;
+ MyParserData *data = user_data;
+
+ maybe_close_starttag (data);
+
+ if (strcmp (element_name, "property") == 0)
+ {
+ g_assert (data->attribute_names == NULL);
+ g_assert (data->attribute_values == NULL);
+ g_assert (data->value == NULL);
+
+ data->attribute_names = g_strdupv ((gchar **)attribute_names);
+ data->attribute_values = g_strdupv ((gchar **)attribute_values);
+ data->value = g_string_new ("");
+
+ return;
+ }
+ else if (strcmp (element_name, "packing") == 0)
+ {
+ data->packing = TRUE;
+ data->packing_started = FALSE;
+
+ return;
+ }
+ else if (strcmp (element_name, "cell-packing") == 0)
+ {
+ data->cell_packing = TRUE;
+ data->cell_packing_started = FALSE;
+
+ return;
+ }
+ else if (strcmp (element_name, "child") == 0)
+ {
+ data->in_child += 1;
+
+ if (attribute_names[0] == NULL)
+ return;
+
+ data->child_started += 1;
+ }
+ else if (strcmp (element_name, "attribute") == 0)
+ {
+ /* attribute in label has no content */
+ if (data->classes == NULL ||
+ strcmp ((gchar *)data->classes->data, "GtkLabel") != 0)
+ data->value = g_string_new ("");
+ }
+ else if (stack_is (context, "item", "items", NULL) ||
+ stack_is (context, "action-widget", "action-widgets", NULL) ||
+ stack_is (context, "mime-type", "mime-types", NULL) ||
+ stack_is (context, "pattern", "patterns", NULL) ||
+ stack_is (context, "application", "applications", NULL) ||
+ stack_is (context, "col", "row", "data", NULL) ||
+ stack_is (context, "mark", "marks", NULL) ||
+ stack_is (context, "action", "accessibility", NULL))
+ {
+ data->value = g_string_new ("");
+ }
+ else if (strcmp (element_name, "placeholder") == 0)
+ {
+ return;
+ }
+ else if (strcmp (element_name, "object") == 0 ||
+ strcmp (element_name, "template") == 0)
+ {
+ maybe_start_child (data);
+
+ for (i = 0; attribute_names[i]; i++)
+ {
+ if (strcmp (attribute_names[i], "class") == 0)
+ {
+ data->classes = g_list_prepend (data->classes,
+ g_strdup (attribute_values[i]));
+ break;
+ }
+ }
+ }
+
+ g_fprintf (data->output, "%*s<%s", data->indent, "", element_name);
+ for (i = 0; attribute_names[i]; i++)
+ {
+ escaped = g_markup_escape_text (attribute_values[i], -1);
+ g_fprintf (data->output, " %s=\"%s\"", attribute_names[i], escaped);
+ g_free (escaped);
+ }
+ data->unclosed_starttag = TRUE;
+ data->indent += 2;
+}
+
+static void
+end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ MyParserData *data = user_data;
+
+ if (strcmp (element_name, "property") == 0)
+ {
+ maybe_emit_property (data);
+
+ g_clear_pointer (&data->attribute_names, g_strfreev);
+ g_clear_pointer (&data->attribute_values, g_strfreev);
+ g_string_free (data->value, TRUE);
+ data->value = NULL;
+ return;
+ }
+ else if (strcmp (element_name, "packing") == 0)
+ {
+ data->packing = FALSE;
+ if (!data->packing_started)
+ return;
+ }
+ else if (strcmp (element_name, "cell-packing") == 0)
+ {
+ data->cell_packing = FALSE;
+ if (!data->cell_packing_started)
+ return;
+ }
+ else if (strcmp (element_name, "child") == 0)
+ {
+ data->in_child -= 1;
+ if (data->child_started == data->in_child)
+ return;
+ data->child_started -= 1;
+ }
+ else if (strcmp (element_name, "placeholder") == 0)
+ {
+ return;
+ }
+ else if (strcmp (element_name, "object") == 0 ||
+ strcmp (element_name, "template") == 0)
+ {
+ g_free (data->classes->data);
+ data->classes = g_list_delete_link (data->classes, data->classes);
+ }
+
+ if (data->value != NULL)
+ {
+ gchar *escaped;
+
+ if (data->unclosed_starttag)
+ g_fprintf (data->output, ">");
+
+ escaped = g_markup_escape_text (data->value->str, -1);
+ g_fprintf (data->output, "%s</%s>\n", escaped, element_name);
+ g_free (escaped);
+
+ g_string_free (data->value, TRUE);
+ data->value = NULL;
+ }
+ else
+ {
+ if (data->unclosed_starttag)
+ g_fprintf (data->output, "/>\n");
+ else
+ g_fprintf (data->output, "%*s</%s>\n", data->indent - 2, "", element_name);
+ }
+
+ data->indent -= 2;
+ data->unclosed_starttag = FALSE;
+}
+
+static void
+text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ MyParserData *data = user_data;
+
+ if (data->value)
+ {
+ g_string_append_len (data->value, text, text_len);
+ return;
+ }
+}
+
+static void
+passthrough (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ MyParserData *data = user_data;
+
+ maybe_close_starttag (data);
+
+ g_fprintf (data->output, "%*s%s\n", data->indent, "", text);
+}
+
+GMarkupParser parser = {
+ start_element,
+ end_element,
+ text,
+ passthrough,
+ NULL
+};
+
+static void
+do_simplify (int *argc,
+ const char ***argv)
+{
+ GMarkupParseContext *context;
+ gchar *buffer;
+ MyParserData data;
+ gboolean replace = FALSE;
+ char **filenames = NULL;
+ GOptionContext *ctx;
+ const GOptionEntry entries[] = {
+ { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
+ { NULL, }
+ };
+ GError *error = NULL;
+
+ ctx = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (ctx, FALSE);
+ g_option_context_add_main_entries (ctx, entries, NULL);
+
+ if (!g_option_context_parse (ctx, argc, (char ***)argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ g_option_context_free (ctx);
+
+ if (filenames == NULL)
+ {
+ g_printerr ("No .ui file specified\n");
+ exit (1);
+ }
+
+ if (g_strv_length (filenames) > 1)
+ {
+ g_printerr ("Can only simplify a single .ui file\n");
+ exit (1);
+ }
+
+ data.input_filename = filenames[0];
+ data.output_filename = NULL;
+
+ if (replace)
+ {
+ int fd;
+ fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL);
+ data.output = fdopen (fd, "w");
+ }
+ else
+ {
+ data.output = stdout;
+ }
+
+ if (!g_file_get_contents (filenames[0], &buffer, NULL, &error))
+ {
+ g_printerr (_("Can’t load file: %s\n"), error->message);
+ exit (1);
+ }
+
+ data.builder = gtk_builder_new ();
+ data.classes = NULL;
+ data.attribute_names = NULL;
+ data.attribute_values = NULL;
+ data.value = NULL;
+ data.packing = FALSE;
+ data.packing_started = FALSE;
+ data.cell_packing = FALSE;
+ data.cell_packing_started = FALSE;
+ data.in_child = 0;
+ data.child_started = 0;
+ data.unclosed_starttag = FALSE;
+ data.indent = 0;
+
+ context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL);
+ if (!g_markup_parse_context_parse (context, buffer, -1, &error))
+ {
+ g_printerr (_("Can’t parse file: %s\n"), error->message);
+ exit (1);
+ }
+
+ fclose (data.output);
+
+ if (data.output_filename)
+ {
+ char *content;
+ gsize length;
+
+ if (!g_file_get_contents (data.output_filename, &content, &length, &error))
+ {
+ g_printerr ("Failed to read %s: %s\n", data.output_filename, error->message);
+ exit (1);
+ }
+
+ if (!g_file_set_contents (data.input_filename, content, length, &error))
+ {
+ g_printerr ("Failed to write %s: %s\n", data.input_filename, error->message);
+ exit (1);
+ }
+ }
+}
+
+static GType
+make_fake_type (const gchar *type_name,
+ const gchar *parent_name)
+{
+ GType parent_type;
+ GTypeQuery query;
+
+ parent_type = g_type_from_name (parent_name);
+ if (parent_type == G_TYPE_INVALID)
+ {
+ g_printerr ("Failed to lookup template parent type %s\n", parent_name);
+ exit (1);
+ }
+
+ g_type_query (parent_type, &query);
+ return g_type_register_static_simple (parent_type,
+ type_name,
+ query.class_size,
+ NULL,
+ query.instance_size,
+ NULL,
+ 0);
+}
+
+static void
+do_validate_template (const gchar *filename,
+ const gchar *type_name,
+ const gchar *parent_name)
+{
+ GType template_type;
+ GtkWidget *widget;
+ GtkBuilder *builder;
+ GError *error = NULL;
+ gint ret;
+
+ /* Only make a fake type if it doesn't exist yet.
+ * This lets us e.g. validate the GtkFileChooserWidget template.
+ */
+ template_type = g_type_from_name (type_name);
+ if (template_type == G_TYPE_INVALID)
+ template_type = make_fake_type (type_name, parent_name);
+
+ widget = g_object_new (template_type, NULL);
+ if (!widget)
+ {
+ g_printerr ("Failed to create an instance of the template type %s\n", type_name);
+ exit (1);
+ }
+
+ builder = gtk_builder_new ();
+ ret = gtk_builder_extend_with_template (builder, widget, template_type, " ", 1, &error);
+ if (ret)
+ ret = gtk_builder_add_from_file (builder, filename, &error);
+ g_object_unref (builder);
+
+ if (ret == 0)
+ {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+}
+
+static gboolean
+parse_template_error (const gchar *message,
+ gchar **class_name,
+ gchar **parent_name)
+{
+ gchar *p;
+
+ if (!strstr (message, "Not expecting to handle a template"))
+ return FALSE;
+
+ p = strstr (message, "(class '");
+ if (p)
+ {
+ *class_name = g_strdup (p + strlen ("(class '"));
+ p = strstr (*class_name, "'");
+ if (p)
+ *p = '\0';
+ }
+ p = strstr (message, ", parent '");
+ if (p)
+ {
+ *parent_name = g_strdup (p + strlen (", parent '"));
+ p = strstr (*parent_name, "'");
+ if (p)
+ *p = '\0';
+ }
+
+ return TRUE;
+}
+
+static void
+do_validate (const gchar *filename)
+{
+ GtkBuilder *builder;
+ GError *error = NULL;
+ gint ret;
+ gchar *class_name = NULL;
+ gchar *parent_name = NULL;
+
+ builder = gtk_builder_new ();
+ ret = gtk_builder_add_from_file (builder, filename, &error);
+ g_object_unref (builder);
+
+ if (ret == 0)
+ {
+ if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) &&
+ parse_template_error (error->message, &class_name, &parent_name))
+ {
+ do_validate_template (filename, class_name, parent_name);
+ }
+ else
+ {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+ }
+}
+
+static const gchar *
+object_get_name (GObject *object)
+{
+ if (GTK_IS_BUILDABLE (object))
+ return gtk_buildable_get_name (GTK_BUILDABLE (object));
+ else
+ return g_object_get_data (object, "gtk-builder-name");
+}
+
+static void
+do_enumerate (const gchar *filename)
+{
+ GtkBuilder *builder;
+ GError *error = NULL;
+ gint ret;
+ GSList *list, *l;
+ GObject *object;
+ const gchar *name;
+
+ builder = gtk_builder_new ();
+ ret = gtk_builder_add_from_file (builder, filename, &error);
+
+ if (ret == 0)
+ {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+
+ list = gtk_builder_get_objects (builder);
+ for (l = list; l; l = l->next)
+ {
+ object = l->data;
+ name = object_get_name (object);
+ if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
+ continue;
+
+ g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object));
+ }
+ g_slist_free (list);
+
+ g_object_unref (builder);
+}
+
+static void
+set_window_title (GtkWindow *window,
+ const char *filename,
+ const char *id)
+{
+ gchar *name;
+ gchar *title;
+
+ name = g_path_get_basename (filename);
+
+ if (id)
+ title = g_strdup_printf ("%s in %s", id, name);
+ else
+ title = g_strdup (name);
+
+ gtk_window_set_title (window, title);
+
+ g_free (title);
+ g_free (name);
+}
+
+static void
+preview_file (const char *filename,
+ const char *id,
+ const char *cssfile)
+{
+ GtkBuilder *builder;
+ GError *error = NULL;
+ GObject *object;
+ GtkWidget *window;
+
+ if (cssfile)
+ {
+ GtkCssProvider *provider;
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_path (provider, cssfile);
+
+ gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+
+ builder = gtk_builder_new ();
+ if (!gtk_builder_add_from_file (builder, filename, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ exit (1);
+ }
+
+ object = NULL;
+
+ if (id)
+ {
+ object = gtk_builder_get_object (builder, id);
+ }
+ else
+ {
+ GSList *objects, *l;
+
+ objects = gtk_builder_get_objects (builder);
+ for (l = objects; l; l = l->next)
+ {
+ GObject *obj = l->data;
+
+ if (GTK_IS_WINDOW (obj))
+ {
+ object = obj;
+ break;
+ }
+ else if (GTK_IS_WIDGET (obj))
+ {
+ if (object == NULL)
+ object = obj;
+ }
+ }
+ g_slist_free (objects);
+ }
+
+ if (object == NULL)
+ {
+ if (id)
+ g_printerr ("No object with ID '%s' found\n", id);
+ else
+ g_printerr ("No previewable object found\n");
+ exit (1);
+ }
+
+ if (!GTK_IS_WIDGET (object))
+ {
+ g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object));
+ exit (1);
+ }
+
+ if (GTK_IS_WINDOW (object))
+ window = GTK_WIDGET (object);
+ else
+ {
+ GtkWidget *widget = GTK_WIDGET (object);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ if (GTK_IS_BUILDABLE (object))
+ id = gtk_buildable_get_name (GTK_BUILDABLE (object));
+
+ set_window_title (GTK_WINDOW (window), filename, id);
+
+ g_object_ref (widget);
+ if (gtk_widget_get_parent (widget) != NULL)
+ gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget);
+ gtk_container_add (GTK_CONTAINER (window), widget);
+ g_object_unref (widget);
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+
+ gtk_main ();
+
+ g_object_unref (builder);
+}
+
+static void
+do_preview (int *argc,
+ const char ***argv)
+{
+ GOptionContext *context;
+ char *id = NULL;
+ char *css = NULL;
+ char **filenames = NULL;
+ const GOptionEntry entries[] = {
+ { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL },
+ { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
+ { NULL, }
+ };
+ GError *error = NULL;
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ if (!g_option_context_parse (context, argc, (char ***)argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ g_option_context_free (context);
+
+ if (filenames == NULL)
+ {
+ g_printerr ("No .ui file specified\n");
+ exit (1);
+ }
+
+ if (g_strv_length (filenames) > 1)
+ {
+ g_printerr ("Can only preview a single .ui file\n");
+ exit (1);
+ }
+
+ preview_file (filenames[0], id, css);
+
+ g_strfreev (filenames);
+ g_free (id);
+ g_free (css);
+}
+
+static void
+usage (void)
+{
+ g_print (_("Usage:\n"
+ " gtk-builder-tool [COMMAND] FILE\n"
+ "\n"
+ "Commands:\n"
+ " validate Validate the file\n"
+ " simplify [OPTIONS] Simplify the file\n"
+ " enumerate List all named objects\n"
+ " preview [OPTIONS] Preview the file\n"
+ "\n"
+ "Simplify Options:\n"
+ " --replace Replace the file\n"
+ "\n"
+ "Preview Options:\n"
+ " --id=ID Preview only the named object\n"
+ " --css=FILE Use style from CSS file\n"
+ "\n"
+ "Perform various tasks on GtkBuilder .ui files.\n"));
+ exit (1);
+}
+
+int
+main (int argc, const char *argv[])
+{
+ g_set_prgname ("gtk-builder-tool");
+
+ gtk_init ();
+
+ gtk_test_register_all_types ();
+
+ if (argc < 3)
+ usage ();
+
+ if (strcmp (argv[2], "--help") == 0)
+ usage ();
+
+ argv++;
+ argc--;
+
+ if (strcmp (argv[0], "validate") == 0)
+ do_validate (argv[1]);
+ else if (strcmp (argv[0], "simplify") == 0)
+ do_simplify (&argc, &argv);
+ else if (strcmp (argv[0], "enumerate") == 0)
+ do_enumerate (argv[1]);
+ else if (strcmp (argv[0], "preview") == 0)
+ do_preview (&argc, &argv);
+ else
+ usage ();
+
+ return 0;
+}
--- /dev/null
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Tomas Bzatek <tbzatek@redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
+#include <gio/gdesktopappinfo.h>
+#endif
+#include <gtk.h>
+
+static gboolean show_version;
+static gchar **args = NULL;
+
+static GOptionEntry entries[] = {
+ { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Show program version"), NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, NULL },
+ { NULL}
+};
+
+int
+main (int argc, char *argv[])
+{
+ GError *error = NULL;
+ GOptionContext *context = NULL;
+ gchar *summary;
+ gchar *app_name;
+#ifdef G_OS_UNIX
+ gchar *desktop_file_name;
+ gchar *bus_name = NULL;
+#endif
+ GAppInfo *info = NULL;
+ GAppLaunchContext *launch_context;
+ GList *l;
+ GFile *f;
+
+ setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+ context =
+ /* Translators: this message will appear immediately after the */
+ /* usage string - Usage: COMMAND [OPTION…] <THIS_MESSAGE> */
+ g_option_context_new (_("APPLICATION [URI…] — launch an APPLICATION"));
+
+ /* Translators: this message will appear after the usage string */
+ /* and before the list of options. */
+ summary = _("Launch an application (specified by its desktop file name),\n"
+ "optionally passing one or more URIs as arguments.");
+ g_option_context_set_summary (context, summary);
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+ g_option_context_parse (context, &argc, &argv, &error);
+
+ g_option_context_free (context);
+
+ if (error != NULL)
+ {
+ g_printerr (_("Error parsing commandline options: %s\n"), error->message);
+ g_printerr ("\n");
+ g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
+ g_printerr ("\n");
+ g_error_free (error);
+ return 1;
+ }
+
+ if (show_version)
+ {
+ g_print ("%d.%d.%d\n",
+ gtk_get_major_version (),
+ gtk_get_minor_version (),
+ gtk_get_micro_version ());
+ return 0;
+ }
+
+ if (!args)
+ {
+ /* Translators: the %s is the program name. This error message */
+ /* means the user is calling gtk-launch without any argument. */
+ g_printerr (_("%s: missing application name"), g_get_prgname ());
+ g_printerr ("\n");
+ g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
+ g_printerr ("\n");
+ return 1;
+ }
+
+
+ gtk_init ();
+
+ app_name = *args;
+#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
+ bus_name = g_strdup (app_name);
+ if (g_str_has_suffix (app_name, ".desktop"))
+ {
+ desktop_file_name = g_strdup (app_name);
+ bus_name[strlen (bus_name) - strlen(".desktop")] = '\0';
+ }
+ else
+ {
+ desktop_file_name = g_strconcat (app_name, ".desktop", NULL);
+ }
+
+ if (!g_dbus_is_name (bus_name))
+ g_clear_pointer (&bus_name, g_free);
+ info = G_APP_INFO (g_desktop_app_info_new (desktop_file_name));
+ g_free (desktop_file_name);
+#else
+#warning Please add support for creating AppInfo from id for your OS
+ g_printerr (_("Creating AppInfo from id not supported on non unix operating systems"));
+#endif
+ args++;
+
+ if (!info)
+ {
+ /* Translators: the first %s is the program name, the second one */
+ /* is the application name. */
+ g_printerr (_("%s: no such application %s"),
+ g_get_prgname (), app_name);
+ g_printerr ("\n");
+ return 2;
+ }
+
+ l = NULL;
+ for (; *args; args++)
+ {
+ f = g_file_new_for_commandline_arg (*args);
+ l = g_list_append (l, f);
+ }
+
+ launch_context = (GAppLaunchContext*) gdk_display_get_app_launch_context (gdk_display_get_default ());
+ if (!g_app_info_launch (info, l, launch_context, &error))
+ {
+ /* Translators: the first %s is the program name, the second one */
+ /* is the error message. */
+ g_printerr (_("%s: error launching application: %s\n"),
+ g_get_prgname (), error->message);
+ return 3;
+ }
+ g_object_unref (info);
+ g_object_unref (launch_context);
+
+#ifdef G_OS_UNIX
+ if (bus_name != NULL)
+ {
+ GDBusConnection *connection;
+ gchar *object_path, *p;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+ object_path = g_strdup_printf ("/%s", bus_name);
+ for (p = object_path; *p != '\0'; p++)
+ if (*p == '.')
+ *p = '/';
+
+ if (connection)
+ g_dbus_connection_call_sync (connection,
+ bus_name,
+ object_path,
+ "org.freedesktop.DBus.Peer",
+ "Ping",
+ NULL, NULL,
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
+ g_clear_pointer (&object_path, g_free);
+ g_clear_object (&connection);
+ g_clear_pointer (&bus_name, g_free);
+ }
+#endif
+ g_list_free_full (l, g_object_unref);
+
+ return 0;
+}
--- /dev/null
+/* Copyright 2015 Timm Bäder
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING. If not,
+ * see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+
+int
+main (int argc, char **argv)
+{
+ GtkSettings *settings;
+ GParamSpec **props;
+ guint n_properties;
+ guint i;
+ int max_prop_name_length = 0;
+ gchar *pattern = NULL;
+
+ gtk_init ();
+
+ if (argc > 1)
+ pattern = argv[1];
+
+ settings = gtk_settings_get_default ();
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties);
+
+ for (i = 0; i < n_properties; i ++)
+ {
+ int len = strlen (props[i]->name);
+
+ if (len > max_prop_name_length)
+ max_prop_name_length = len;
+ }
+
+
+ for (i = 0; i < n_properties; i ++)
+ {
+ GValue value = {0};
+ GParamSpec *prop = props[i];
+ gchar *value_str;
+ int spacing = max_prop_name_length - strlen (prop->name) + 1;
+ gboolean deprecated;
+
+ if (pattern && !g_strrstr (prop->name, pattern))
+ continue;
+
+ g_value_init (&value, prop->value_type);
+ g_object_get_property (G_OBJECT (settings), prop->name, &value);
+ deprecated = prop->flags & G_PARAM_DEPRECATED;
+
+ if (G_VALUE_HOLDS_ENUM (&value))
+ {
+ GEnumClass *enum_class = G_PARAM_SPEC_ENUM (prop)->enum_class;
+ GEnumValue *enum_value = g_enum_get_value (enum_class, g_value_get_enum (&value));
+
+ value_str = g_strdup (enum_value->value_name);
+ }
+ else
+ {
+ value_str = g_strdup_value_contents (&value);
+ }
+
+ if (deprecated)
+ {
+ printf ("!");
+ spacing --;
+ }
+
+ for (; spacing >= 0; spacing --)
+ printf (" ");
+
+ printf ("%s: %s\n", prop->name, value_str);
+
+ g_free (value_str);
+ g_value_unset (&value);
+ }
+
+ g_free (props);
+
+ return 0;
+}
--- /dev/null
+<?xml version="1.0"?>
+<its:rules xmlns:its="http://www.w3.org/2005/11/its"
+ xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
+ version="2.0">
+ <its:translateRule selector="/interface" translate="no"/>
+ <its:translateRule selector="/interface//*[@translatable = 'yes']"
+ translate="yes"/>
+
+ <!-- The 'comment' attribute should be extracted as a translator comment. -->
+ <its:locNoteRule selector="/interface//*[@comments]"
+ locNotePointer="@comments"
+ locNoteType="alert"/>
+ <gt:escapeRule selector="/interface//@comments" escape="no"/>
+
+ <!-- The 'context' attribute should be extracted as msgctxt. -->
+ <gt:contextRule selector="/interface//*[@context]" contextPointer="@context"/>
+
+ <its:preserveSpaceRule selector="/interface" space="preserve"/>
+
+ <!-- Extracted strings are consumed by the library and are never
+ merged back; we don't want to escape special characters. -->
+ <gt:escapeRule selector="/interface" escape="no"/>
+</its:rules>
--- /dev/null
+<?xml version="1.0"?>
+<locatingRules>
+ <locatingRule name="GtkBuilder" pattern="*.ui">
+ <documentRule localName="interface" target="gtkbuilder.its"/>
+ </locatingRule>
+</locatingRules>
--- /dev/null
+<?xml version="1.0"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="">
+ <start>
+ <element name="interface">
+ <optional>
+ <attribute name="domain">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="requires"/>
+ <ref name="object"/>
+ <ref name="template"/>
+ <ref name="menu"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </start>
+ <define name="requires">
+ <element name="requires">
+ <attribute name="lib">
+ <text/>
+ </attribute>
+ <attribute name="version">
+ <text/>
+ </attribute>
+ </element>
+ </define>
+ <define name="object">
+ <element name="object">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <attribute name="class">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="type-func">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="constructor">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="property"/>
+ <ref name="signal"/>
+ <ref name="child"/>
+ <ref name="ANY"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="template">
+ <element name="template">
+ <attribute name="class">
+ <text/>
+ </attribute>
+ <attribute name="parent">
+ <text/>
+ </attribute>
+ <zeroOrMore>
+ <choice>
+ <ref name="property"/>
+ <ref name="signal"/>
+ <ref name="child"/>
+ <ref name="ANY"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="property">
+ <element name="property">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="translatable">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="comments">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="context">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <group>
+ <attribute name="bind-source">
+ <text/>
+ </attribute>
+ <attribute name="bind-property">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="bind-flags">
+ <text/>
+ </attribute>
+ </optional>
+ </group>
+ </optional>
+ <optional>
+ <text/>
+ </optional>
+ </element>
+ </define>
+ <define name="signal">
+ <element name="signal">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <attribute name="handler">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="after">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="swapped">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="object">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="last_modification_time">
+ <text/>
+ </attribute>
+ </optional>
+ <empty/>
+ </element>
+ </define>
+ <define name="child">
+ <element name="child">
+ <optional>
+ <attribute name="type">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="internal-child">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="object"/>
+ <ref name="ANY"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="menu">
+ <element name="menu">
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ <optional>
+ <attribute name="domain">
+ <text/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="item"/>
+ <ref name="submenu"/>
+ <ref name="section"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="item">
+ <element name="item">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="attribute_"/>
+ <ref name="link"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="attribute_">
+ <element name="attribute">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <optional>
+ <attribute name="type">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="translatable">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="context">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="comments">
+ <text/>
+ </attribute>
+ </optional>
+ <optional>
+ <text/>
+ </optional>
+ </element>
+ </define>
+ <define name="link">
+ <element name="link">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <attribute name="name">
+ <text/>
+ </attribute>
+ <zeroOrMore>
+ <ref name="item"/>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="submenu">
+ <element name="submenu">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="attribute_"/>
+ <ref name="item"/>
+ <ref name="submenu"/>
+ <ref name="section"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="section">
+ <element name="section">
+ <optional>
+ <attribute name="id">
+ <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+ </attribute>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <ref name="attribute_"/>
+ <ref name="item"/>
+ <ref name="submenu"/>
+ <ref name="section"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+ <define name="ANY">
+ <element>
+ <anyName>
+ <except>
+ <name>interface</name>
+ <name>requires</name>
+ <name>object</name>
+ <name>property</name>
+ <name>signal</name>
+ <name>child</name>
+ <name>menu</name>
+ <name>item</name>
+ <name>attribute</name>
+ <name>link</name>
+ <name>submenu</name>
+ <name>section</name>
+ </except>
+ </anyName>
+ <zeroOrMore>
+ <attribute>
+ <anyName/>
+ <text/>
+ </attribute>
+ </zeroOrMore>
+ <interleave>
+ <zeroOrMore>
+ <ref name="ALL"/>
+ </zeroOrMore>
+ <optional>
+ <text/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+ <define name="ALL">
+ <element>
+ <anyName/>
+ <zeroOrMore>
+ <attribute>
+ <anyName/>
+ <text/>
+ </attribute>
+ </zeroOrMore>
+ <interleave>
+ <zeroOrMore>
+ <ref name="ALL"/>
+ </zeroOrMore>
+ <optional>
+ <text/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+</grammar>
--- /dev/null
+/* gtkiconcachevalidator.c
+ * Copyright (C) 2007 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+#include "gtkiconcachevalidatorprivate.h"
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+
+#define VERBOSE(x)
+
+#define check(name,condition) \
+ if (!(condition)) \
+ { \
+ VERBOSE(g_message ("bad %s", (name))); \
+ return FALSE; \
+ }
+
+static inline gboolean
+get_uint16 (CacheInfo *info,
+ guint32 offset,
+ guint16 *value)
+{
+ if (offset < info->cache_size)
+ {
+ *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
+ return TRUE;
+ }
+ else
+ {
+ *value = 0;
+ return FALSE;
+ }
+}
+
+static inline gboolean
+get_uint32 (CacheInfo *info,
+ guint32 offset,
+ guint32 *value)
+{
+ if (offset < info->cache_size)
+ {
+ *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
+ return TRUE;
+ }
+ else
+ {
+ *value = 0;
+ return FALSE;
+ }
+}
+
+static gboolean
+check_version (CacheInfo *info)
+{
+ guint16 major, minor;
+
+ check ("major version", get_uint16 (info, 0, &major) && major == 1);
+ check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
+
+ return TRUE;
+}
+
+static gboolean
+check_string (CacheInfo *info,
+ guint32 offset)
+{
+ check ("string offset", offset < info->cache_size);
+
+ if (info->flags & CHECK_STRINGS)
+ {
+ gint i;
+ gchar c;
+
+ /* assume no string is longer than 1k */
+ for (i = 0; i < 1024; i++)
+ {
+ check ("string offset", offset + i < info->cache_size)
+ c = *(info->cache + offset + i);
+ if (c == '\0')
+ break;
+ check ("string content", g_ascii_isgraph (c));
+ }
+ check ("string length", i < 1024);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_string_utf8 (CacheInfo *info,
+ guint32 offset)
+{
+ check ("string offset", offset < info->cache_size);
+
+ if (info->flags & CHECK_STRINGS)
+ {
+ gint i;
+ gchar c;
+
+ /* assume no string is longer than 1k */
+ for (i = 0; i < 1024; i++)
+ {
+ check ("string offset", offset + i < info->cache_size)
+ c = *(info->cache + offset + i);
+ if (c == '\0')
+ break;
+ }
+ check ("string length", i < 1024);
+ check ("string utf8 data", g_utf8_validate((char *)(info->cache + offset), -1, NULL));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_directory_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 directory_offset;
+ gint i;
+
+ check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
+
+ for (i = 0; i < info->n_directories; i++)
+ {
+ check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
+ if (!check_string (info, directory_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_pixel_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 type;
+ guint32 length;
+
+ check ("offset, pixel data type", get_uint32 (info, offset, &type));
+ check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
+
+ check ("pixel data type", type == 0);
+ check ("pixel data length", offset + 8 + length < info->cache_size);
+
+ if (info->flags & CHECK_PIXBUFS)
+ {
+ GdkPixdata data;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ check ("pixel data", gdk_pixdata_deserialize (&data, length,
+ (const guint8*)info->cache + offset + 8,
+ NULL));
+G_GNUC_END_IGNORE_DEPRECATIONS;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_embedded_rect (CacheInfo *info,
+ guint32 offset)
+{
+ check ("embedded rect", offset + 4 < info->cache_size);
+
+ return TRUE;
+}
+
+static gboolean
+check_attach_point_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_attach_points;
+
+ check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
+ check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
+
+ return TRUE;
+}
+
+static gboolean
+check_display_name_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_display_names, ofs;
+ gint i;
+
+ check ("offset, display name list",
+ get_uint32 (info, offset, &n_display_names));
+ for (i = 0; i < n_display_names; i++)
+ {
+ get_uint32(info, offset + 4 + 8 * i, &ofs);
+ if (!check_string (info, ofs))
+ return FALSE;
+ get_uint32(info, offset + 4 + 8 * i + 4, &ofs);
+ if (!check_string_utf8 (info, ofs))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_meta_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 embedded_rect_offset;
+ guint32 attach_point_list_offset;
+ guint32 display_name_list_offset;
+
+ check ("offset, embedded rect",
+ get_uint32 (info, offset, &embedded_rect_offset));
+ check ("offset, attach point list",
+ get_uint32 (info, offset + 4, &attach_point_list_offset));
+ check ("offset, display name list",
+ get_uint32 (info, offset + 8, &display_name_list_offset));
+
+ if (embedded_rect_offset != 0)
+ {
+ if (!check_embedded_rect (info, embedded_rect_offset))
+ return FALSE;
+ }
+
+ if (attach_point_list_offset != 0)
+ {
+ if (!check_attach_point_list (info, attach_point_list_offset))
+ return FALSE;
+ }
+
+ if (display_name_list_offset != 0)
+ {
+ if (!check_display_name_list (info, display_name_list_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image_data (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 pixel_data_offset;
+ guint32 meta_data_offset;
+
+ check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
+ check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
+
+ if (pixel_data_offset != 0)
+ {
+ if (!check_pixel_data (info, pixel_data_offset))
+ return FALSE;
+ }
+ if (meta_data_offset != 0)
+ {
+ if (!check_meta_data (info, meta_data_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image (CacheInfo *info,
+ guint32 offset)
+{
+ guint16 index;
+ guint16 flags;
+ guint32 image_data_offset;
+
+ check ("offset, image index", get_uint16 (info, offset, &index));
+ check ("offset, image flags", get_uint16 (info, offset + 2, &flags));
+ check ("offset, image data offset",
+ get_uint32 (info, offset + 4, &image_data_offset));
+
+ check ("image index", index < info->n_directories);
+ check ("image flags", flags < 16);
+
+ if (image_data_offset != 0)
+ {
+ if (!check_image_data (info, image_data_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_image_list (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_images;
+ gint i;
+
+ check ("offset, image list", get_uint32 (info, offset, &n_images));
+
+ for (i = 0; i < n_images; i++)
+ {
+ if (!check_image (info, offset + 4 + 8 * i))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_icon (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 chain_offset;
+ guint32 name_offset;
+ guint32 image_list_offset;
+
+ check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
+ check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
+ check ("offset, icon image list", get_uint32 (info, offset + 8,
+ &image_list_offset));
+
+ if (!check_string (info, name_offset))
+ return FALSE;
+ if (!check_image_list (info, image_list_offset))
+ return FALSE;
+ if (chain_offset != 0xffffffff)
+ {
+ if (!check_icon (info, chain_offset))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_hash (CacheInfo *info,
+ guint32 offset)
+{
+ guint32 n_buckets, icon_offset;
+ gint i;
+
+ check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
+
+ for (i = 0; i < n_buckets; i++)
+ {
+ check ("offset, hash chain",
+ get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
+ if (icon_offset != 0xffffffff)
+ {
+ if (!check_icon (info, icon_offset))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gtk_icon_cache_validate:
+ * @info: a CacheInfo structure
+ *
+ * Validates the icon cache passed in the @cache and
+ * @cache_size fields of the @info structure. The
+ * validator checks that offsets specified in the
+ * cache do not point outside the mapped area, that
+ * strings look reasonable, and that pixbufs can
+ * be deserialized. The amount of validation can
+ * be controlled with the @flags field.
+ *
+ * Returns: %TRUE if the cache is valid
+ */
+gboolean
+gtk_icon_cache_validate (CacheInfo *info)
+{
+ guint32 hash_offset;
+ guint32 directory_list_offset;
+
+ if (!check_version (info))
+ return FALSE;
+ check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
+ check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
+ if (!check_directory_list (info, directory_list_offset))
+ return FALSE;
+
+ if (!check_hash (info, hash_offset))
+ return FALSE;
+
+ return TRUE;
+}
+
--- /dev/null
+# Installed tools
+gtk_tools = [
+ ['gtk4-query-settings', ['gtk-query-settings.c']],
+ ['gtk4-builder-tool', ['gtk-builder-tool.c']],
+ ['gtk4-update-icon-cache', ['updateiconcache.c', 'gtkiconcachevalidator.c']],
+ ['gtk4-encode-symbolic-svg', ['encodesymbolic.c', 'gdkpixbufutils.c']],
+]
+
+if os_unix
+ gtk_tools += [['gtk4-launch', ['gtk-launch.c']]]
+endif
+
+foreach tool: gtk_tools
+ tool_name = tool.get(0)
+ tool_srcs = tool.get(1)
+
+ exe = executable(tool_name, tool_srcs,
+ include_directories: [confinc],
+ c_args: gtk_cargs,
+ dependencies: libgtk_dep,
+ install: true)
+
+ set_variable(tool_name.underscorify(), exe) # used in testsuites
+endforeach
+
+# Data to install
+install_data('gtkbuilder.rng',
+ install_dir: join_paths(gtk_datadir, 'gtk-4.0'))
+
+install_data('gtkbuilder.loc', 'gtkbuilder.its',
+ install_dir: join_paths(gtk_datadir, 'gettext/its'))
+
--- /dev/null
+/* updateiconcache.c
+ * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#ifdef _MSC_VER
+#include <io.h>
+#include <sys/utime.h>
+#else
+#include <utime.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#include <glib/gi18n.h>
+#include "gtkiconcachevalidatorprivate.h"
+
+static gboolean force_update = FALSE;
+static gboolean ignore_theme_index = FALSE;
+static gboolean quiet = FALSE;
+static gboolean index_only = TRUE;
+static gboolean validate = FALSE;
+static gchar *var_name = (gchar *) "-";
+
+#define CACHE_NAME "icon-theme.cache"
+
+#define HAS_SUFFIX_XPM (1 << 0)
+#define HAS_SUFFIX_SVG (1 << 1)
+#define HAS_SUFFIX_PNG (1 << 2)
+#define HAS_ICON_FILE (1 << 3)
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 0
+#define HASH_OFFSET 12
+
+#define ALIGN_VALUE(this, boundary) \
+ (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
+
+#ifdef HAVE_FTW_H
+
+#include <ftw.h>
+
+static GStatBuf cache_dir_stat;
+static gboolean cache_up_to_date;
+
+static int check_dir_mtime (const char *dir,
+ const struct stat *sb,
+ int tf)
+{
+ if (tf != FTW_NS && sb->st_mtime > cache_dir_stat.st_mtime)
+ {
+ cache_up_to_date = FALSE;
+ /* stop tree walk */
+ return 1;
+ }
+
+ return 0;
+}
+
+static gboolean
+is_cache_up_to_date (const gchar *path)
+{
+ gchar *cache_path;
+ gint retval;
+
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+ retval = g_stat (cache_path, &cache_dir_stat);
+ g_free (cache_path);
+
+ if (retval < 0)
+ {
+ /* Cache file not found */
+ return FALSE;
+ }
+
+ cache_up_to_date = TRUE;
+
+ ftw (path, check_dir_mtime, 20);
+
+ return cache_up_to_date;
+}
+
+#else /* !HAVE_FTW_H */
+
+gboolean
+is_cache_up_to_date (const gchar *path)
+{
+ GStatBuf path_stat, cache_stat;
+ gchar *cache_path;
+ int retval;
+
+ retval = g_stat (path, &path_stat);
+
+ if (retval < 0)
+ {
+ /* We can't stat the path,
+ * assume we have a updated cache */
+ return TRUE;
+ }
+
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+ retval = g_stat (cache_path, &cache_stat);
+ g_free (cache_path);
+
+ if (retval < 0)
+ {
+ /* Cache file not found */
+ return FALSE;
+ }
+
+ /* Check mtime */
+ return cache_stat.st_mtime >= path_stat.st_mtime;
+}
+
+#endif /* !HAVE_FTW_H */
+
+static gboolean
+has_theme_index (const gchar *path)
+{
+ gboolean result;
+ gchar *index_path;
+
+ index_path = g_build_filename (path, "index.theme", NULL);
+
+ result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
+
+ g_free (index_path);
+
+ return result;
+}
+
+
+typedef struct
+{
+ GdkPixdata pixdata;
+ gboolean has_pixdata;
+ guint32 offset;
+ guint size;
+} ImageData;
+
+typedef struct
+{
+ int has_embedded_rect;
+ int x0, y0, x1, y1;
+
+ int n_attach_points;
+ int *attach_points;
+
+ int n_display_names;
+ char **display_names;
+
+ guint32 offset;
+ gint size;
+} IconData;
+
+static GHashTable *image_data_hash = NULL;
+static GHashTable *icon_data_hash = NULL;
+
+typedef struct
+{
+ int flags;
+ int dir_index;
+
+ ImageData *image_data;
+ guint pixel_data_size;
+
+ IconData *icon_data;
+ guint icon_data_size;
+} Image;
+
+
+static gboolean
+foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
+{
+ Image *image = (Image *)value;
+ GHashTable *files = user_data;
+ GList *list;
+ gboolean free_key = FALSE;
+
+ if (image->flags == HAS_ICON_FILE)
+ {
+ /* just a .icon file, throw away */
+ g_free (key);
+ g_free (image);
+
+ return TRUE;
+ }
+
+ list = g_hash_table_lookup (files, key);
+ if (list)
+ free_key = TRUE;
+
+ list = g_list_prepend (list, value);
+ g_hash_table_insert (files, key, list);
+
+ if (free_key)
+ g_free (key);
+
+ return TRUE;
+}
+
+static IconData *
+load_icon_data (const char *path)
+{
+ GKeyFile *icon_file;
+ char **split;
+ gsize length;
+ char *str;
+ char *split_point;
+ int i;
+ gint *ivalues;
+ GError *error = NULL;
+ gchar **keys;
+ gsize n_keys;
+ IconData *data;
+
+ icon_file = g_key_file_new ();
+ g_key_file_set_list_separator (icon_file, ',');
+ g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+ if (error)
+ {
+ g_error_free (error);
+ g_key_file_free (icon_file);
+
+ return NULL;
+ }
+
+ data = g_new0 (IconData, 1);
+
+ ivalues = g_key_file_get_integer_list (icon_file,
+ "Icon Data", "EmbeddedTextRectangle",
+ &length, NULL);
+ if (ivalues)
+ {
+ if (length == 4)
+ {
+ data->has_embedded_rect = TRUE;
+ data->x0 = ivalues[0];
+ data->y0 = ivalues[1];
+ data->x1 = ivalues[2];
+ data->y1 = ivalues[3];
+ }
+
+ g_free (ivalues);
+ }
+
+ str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
+ if (str)
+ {
+ split = g_strsplit (str, "|", -1);
+
+ data->n_attach_points = g_strv_length (split);
+ data->attach_points = g_new (int, 2 * data->n_attach_points);
+
+ for (i = 0; i < data->n_attach_points; ++i)
+ {
+ split_point = strchr (split[i], ',');
+ if (split_point)
+ {
+ *split_point = 0;
+ split_point++;
+ data->attach_points[2 * i] = atoi (split[i]);
+ data->attach_points[2 * i + 1] = atoi (split_point);
+ }
+ }
+
+ g_strfreev (split);
+ g_free (str);
+ }
+
+ keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
+ data->display_names = g_new0 (gchar *, 2 * n_keys + 1);
+ data->n_display_names = 0;
+
+ for (i = 0; i < n_keys; i++)
+ {
+ gchar *lang, *name;
+
+ if (g_str_has_prefix (keys[i], "DisplayName"))
+ {
+ gchar *open, *close = NULL;
+
+ open = strchr (keys[i], '[');
+
+ if (open)
+ close = strchr (open, ']');
+
+ if (open && close)
+ {
+ lang = g_strndup (open + 1, close - open - 1);
+ name = g_key_file_get_locale_string (icon_file,
+ "Icon Data", "DisplayName",
+ lang, NULL);
+ }
+ else
+ {
+ lang = g_strdup ("C");
+ name = g_key_file_get_string (icon_file,
+ "Icon Data", "DisplayName",
+ NULL);
+ }
+
+ data->display_names[2 * data->n_display_names] = lang;
+ data->display_names[2 * data->n_display_names + 1] = name;
+ data->n_display_names++;
+ }
+ }
+
+ g_strfreev (keys);
+
+ g_key_file_free (icon_file);
+
+ /* -1 means not computed yet, the real value depends
+ * on string pool state, and will be computed
+ * later
+ */
+ data->size = -1;
+
+ return data;
+}
+
+/*
+ * This function was copied from gtkfilesystemunix.c, it should
+ * probably go to GLib
+ */
+static void
+canonicalize_filename (gchar *filename)
+{
+ gchar *p, *q;
+ gboolean last_was_slash = FALSE;
+
+ p = filename;
+ q = filename;
+
+ while (*p)
+ {
+ if (*p == G_DIR_SEPARATOR)
+ {
+ if (!last_was_slash)
+ *q++ = G_DIR_SEPARATOR;
+
+ last_was_slash = TRUE;
+ }
+ else
+ {
+ if (last_was_slash && *p == '.')
+ {
+ if (*(p + 1) == G_DIR_SEPARATOR ||
+ *(p + 1) == '\0')
+ {
+ if (*(p + 1) == '\0')
+ break;
+
+ p += 1;
+ }
+ else if (*(p + 1) == '.' &&
+ (*(p + 2) == G_DIR_SEPARATOR ||
+ *(p + 2) == '\0'))
+ {
+ if (q > filename + 1)
+ {
+ q--;
+ while (q > filename + 1 &&
+ *(q - 1) != G_DIR_SEPARATOR)
+ q--;
+ }
+
+ if (*(p + 2) == '\0')
+ break;
+
+ p += 2;
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+
+ p++;
+ }
+
+ if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
+ q--;
+
+ *q = '\0';
+}
+
+static gchar *
+follow_links (const gchar *path)
+{
+ gchar *target;
+ gchar *d, *s;
+ gchar *path2 = NULL;
+
+ path2 = g_strdup (path);
+ while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
+ {
+ target = g_file_read_link (path2, NULL);
+
+ if (target)
+ {
+ if (g_path_is_absolute (target))
+ path2 = target;
+ else
+ {
+ d = g_path_get_dirname (path2);
+ s = g_build_filename (d, target, NULL);
+ g_free (d);
+ g_free (target);
+ g_free (path2);
+ path2 = s;
+ }
+ }
+ else
+ break;
+ }
+
+ if (strcmp (path, path2) == 0)
+ {
+ g_free (path2);
+ path2 = NULL;
+ }
+
+ return path2;
+}
+
+static void
+maybe_cache_image_data (Image *image,
+ const gchar *path)
+{
+ if (!index_only && !image->image_data &&
+ (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
+ {
+ GdkPixbuf *pixbuf;
+ ImageData *idata;
+ gchar *path2;
+
+ idata = g_hash_table_lookup (image_data_hash, path);
+ path2 = follow_links (path);
+
+ if (path2)
+ {
+ ImageData *idata2;
+
+ canonicalize_filename (path2);
+
+ idata2 = g_hash_table_lookup (image_data_hash, path2);
+
+ if (idata && idata2 && idata != idata2)
+ g_error ("different idatas found for symlinked '%s' and '%s'\n",
+ path, path2);
+
+ if (idata && !idata2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+
+ if (!idata && idata2)
+ {
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
+ idata = idata2;
+ }
+ }
+
+ if (!idata)
+ {
+ idata = g_new0 (ImageData, 1);
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata);
+ if (path2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+ }
+
+ if (!idata->has_pixdata)
+ {
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+
+ if (pixbuf)
+ {
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
+G_GNUC_END_IGNORE_DEPRECATIONS;
+ idata->size = idata->pixdata.length + 8;
+ idata->has_pixdata = TRUE;
+ }
+ }
+
+ image->image_data = idata;
+
+ g_free (path2);
+ }
+}
+
+static void
+maybe_cache_icon_data (Image *image,
+ const gchar *path)
+{
+ if (g_str_has_suffix (path, ".icon"))
+ {
+ IconData *idata = NULL;
+ gchar *path2 = NULL;
+
+ idata = g_hash_table_lookup (icon_data_hash, path);
+ path2 = follow_links (path);
+
+ if (path2)
+ {
+ IconData *idata2;
+
+ canonicalize_filename (path2);
+
+ idata2 = g_hash_table_lookup (icon_data_hash, path2);
+
+ if (idata && idata2 && idata != idata2)
+ g_error ("different idatas found for symlinked '%s' and '%s'\n",
+ path, path2);
+
+ if (idata && !idata2)
+ g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+
+ if (!idata && idata2)
+ {
+ g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
+ idata = idata2;
+ }
+ }
+
+ if (!idata)
+ {
+ idata = load_icon_data (path);
+ g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
+ if (path2)
+ g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+ }
+
+ image->icon_data = idata;
+
+ g_free (path2);
+ }
+}
+
+/*
+ * Finds all dir separators and replaces them with “/”.
+ * This makes sure that only /-separated paths are written in cache files,
+ * maintaining compatibility with theme index files that use slashes as
+ * directory separators on all platforms.
+ */
+static void
+replace_backslashes_with_slashes (gchar *path)
+{
+ size_t i;
+ if (path == NULL)
+ return;
+ for (i = 0; path[i]; i++)
+ if (G_IS_DIR_SEPARATOR (path[i]))
+ path[i] = '/';
+}
+
+static GList *
+scan_directory (const gchar *base_path,
+ const gchar *subdir,
+ GHashTable *files,
+ GList *directories,
+ gint depth)
+{
+ GHashTable *dir_hash;
+ GDir *dir;
+ GList *list = NULL, *iterator = NULL;
+ const gchar *name;
+ gchar *dir_path;
+ gboolean dir_added = FALSE;
+ guint dir_index = 0xffff;
+
+ dir_path = g_build_path ("/", base_path, subdir, NULL);
+
+ /* FIXME: Use the gerror */
+ dir = g_dir_open (dir_path, 0, NULL);
+
+ if (!dir)
+ return directories;
+
+ dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ while ((name = g_dir_read_name (dir)))
+ {
+ list = g_list_prepend (list, g_strdup (name));
+ }
+ list = g_list_sort (list, (GCompareFunc) strcmp);
+ for (iterator = list; iterator; iterator = iterator->next)
+ {
+ name = iterator->data;
+
+ gchar *path;
+ gboolean retval;
+ int flags = 0;
+ Image *image;
+ gchar *basename, *dot;
+
+ path = g_build_filename (dir_path, name, NULL);
+
+ retval = g_file_test (path, G_FILE_TEST_IS_DIR);
+ if (retval)
+ {
+ gchar *subsubdir;
+
+ if (subdir)
+ subsubdir = g_build_path ("/", subdir, name, NULL);
+ else
+ subsubdir = g_strdup (name);
+ directories = scan_directory (base_path, subsubdir, files,
+ directories, depth + 1);
+ g_free (subsubdir);
+
+ continue;
+ }
+
+ /* ignore images in the toplevel directory */
+ if (subdir == NULL)
+ continue;
+
+ retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
+ if (retval)
+ {
+ if (g_str_has_suffix (name, ".png"))
+ flags |= HAS_SUFFIX_PNG;
+ else if (g_str_has_suffix (name, ".svg"))
+ flags |= HAS_SUFFIX_SVG;
+ else if (g_str_has_suffix (name, ".xpm"))
+ flags |= HAS_SUFFIX_XPM;
+ else if (g_str_has_suffix (name, ".icon"))
+ flags |= HAS_ICON_FILE;
+
+ if (flags == 0)
+ continue;
+
+ basename = g_strdup (name);
+ dot = strrchr (basename, '.');
+ *dot = '\0';
+
+ image = g_hash_table_lookup (dir_hash, basename);
+ if (!image)
+ {
+ if (!dir_added)
+ {
+ dir_added = TRUE;
+ if (subdir)
+ {
+ dir_index = g_list_length (directories);
+ directories = g_list_append (directories, g_strdup (subdir));
+ }
+ else
+ dir_index = 0xffff;
+ }
+
+ image = g_new0 (Image, 1);
+ image->dir_index = dir_index;
+ g_hash_table_insert (dir_hash, g_strdup (basename), image);
+ }
+
+ image->flags |= flags;
+
+ maybe_cache_image_data (image, path);
+ maybe_cache_icon_data (image, path);
+
+ g_free (basename);
+ }
+
+ g_free (path);
+ }
+
+ g_list_free_full (list, g_free);
+ g_dir_close (dir);
+
+ /* Move dir into the big file hash */
+ g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
+
+ g_hash_table_destroy (dir_hash);
+
+ return directories;
+}
+
+typedef struct _HashNode HashNode;
+
+struct _HashNode
+{
+ HashNode *next;
+ gchar *name;
+ GList *image_list;
+ gint offset;
+};
+
+static guint
+icon_name_hash (gconstpointer key)
+{
+ const signed char *p = key;
+ guint32 h = *p;
+
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+typedef struct {
+ gint size;
+ HashNode **nodes;
+} HashContext;
+
+static gboolean
+convert_to_hash (gpointer key, gpointer value, gpointer user_data)
+{
+ HashContext *context = user_data;
+ guint hash;
+ HashNode *node;
+
+ hash = icon_name_hash (key) % context->size;
+
+ node = g_new0 (HashNode, 1);
+ node->next = NULL;
+ node->name = key;
+ node->image_list = value;
+
+ if (context->nodes[hash] != NULL)
+ node->next = context->nodes[hash];
+
+ context->nodes[hash] = node;
+
+ return TRUE;
+}
+
+static GHashTable *string_pool = NULL;
+
+static int
+find_string (const gchar *n)
+{
+ return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
+}
+
+static void
+add_string (const gchar *n, int offset)
+{
+ g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
+}
+
+static gboolean
+write_string (FILE *cache, const gchar *n)
+{
+ gchar *s;
+ int i, l;
+
+ l = ALIGN_VALUE (strlen (n) + 1, 4);
+
+ s = g_malloc0 (l);
+ strcpy (s, n);
+
+ i = fwrite (s, l, 1, cache);
+
+ g_free (s);
+
+ return i == 1;
+
+}
+
+static gboolean
+write_card16 (FILE *cache, guint16 n)
+{
+ int i;
+
+ n = GUINT16_TO_BE (n);
+
+ i = fwrite ((char *)&n, 2, 1, cache);
+
+ return i == 1;
+}
+
+static gboolean
+write_card32 (FILE *cache, guint32 n)
+{
+ int i;
+
+ n = GUINT32_TO_BE (n);
+
+ i = fwrite ((char *)&n, 4, 1, cache);
+
+ return i == 1;
+}
+
+
+static gboolean
+write_image_data (FILE *cache, ImageData *image_data, int offset)
+{
+ guint8 *s;
+ guint len;
+ gint i;
+ GdkPixdata *pixdata = &image_data->pixdata;
+
+ /* Type 0 is GdkPixdata */
+ if (!write_card32 (cache, 0))
+ return FALSE;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ s = gdk_pixdata_serialize (pixdata, &len);
+G_GNUC_END_IGNORE_DEPRECATIONS;
+
+ if (!write_card32 (cache, len))
+ {
+ g_free (s);
+ return FALSE;
+ }
+
+ i = fwrite (s, len, 1, cache);
+
+ g_free (s);
+
+ return i == 1;
+}
+
+static gboolean
+write_icon_data (FILE *cache, IconData *icon_data, int offset)
+{
+ int ofs = offset + 12;
+ int j;
+ int tmp, tmp2;
+
+ if (icon_data->has_embedded_rect)
+ {
+ if (!write_card32 (cache, ofs))
+ return FALSE;
+
+ ofs += 8;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ if (icon_data->n_attach_points > 0)
+ {
+ if (!write_card32 (cache, ofs))
+ return FALSE;
+
+ ofs += 4 + 4 * icon_data->n_attach_points;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ if (icon_data->n_display_names > 0)
+ {
+ if (!write_card32 (cache, ofs))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ if (icon_data->has_embedded_rect)
+ {
+ if (!write_card16 (cache, icon_data->x0) ||
+ !write_card16 (cache, icon_data->y0) ||
+ !write_card16 (cache, icon_data->x1) ||
+ !write_card16 (cache, icon_data->y1))
+ return FALSE;
+ }
+
+ if (icon_data->n_attach_points > 0)
+ {
+ if (!write_card32 (cache, icon_data->n_attach_points))
+ return FALSE;
+
+ for (j = 0; j < 2 * icon_data->n_attach_points; j++)
+ {
+ if (!write_card16 (cache, icon_data->attach_points[j]))
+ return FALSE;
+ }
+ }
+
+ if (icon_data->n_display_names > 0)
+ {
+ if (!write_card32 (cache, icon_data->n_display_names))
+ return FALSE;
+
+ ofs += 4 + 8 * icon_data->n_display_names;
+
+ tmp = ofs;
+ for (j = 0; j < 2 * icon_data->n_display_names; j++)
+ {
+ tmp2 = find_string (icon_data->display_names[j]);
+ if (tmp2 == 0 || tmp2 == -1)
+ {
+ tmp2 = tmp;
+ tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
+ /* We're playing a little game with negative
+ * offsets here to handle duplicate strings in
+ * the array.
+ */
+ add_string (icon_data->display_names[j], -tmp2);
+ }
+ else if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ }
+
+ if (!write_card32 (cache, tmp2))
+ return FALSE;
+
+ }
+
+ g_assert (ofs == ftell (cache));
+ for (j = 0; j < 2 * icon_data->n_display_names; j++)
+ {
+ tmp2 = find_string (icon_data->display_names[j]);
+ g_assert (tmp2 != 0 && tmp2 != -1);
+ if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ g_assert (tmp2 == ftell (cache));
+ add_string (icon_data->display_names[j], tmp2);
+ if (!write_string (cache, icon_data->display_names[j]))
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_header (FILE *cache, guint32 dir_list_offset)
+{
+ return (write_card16 (cache, MAJOR_VERSION) &&
+ write_card16 (cache, MINOR_VERSION) &&
+ write_card32 (cache, HASH_OFFSET) &&
+ write_card32 (cache, dir_list_offset));
+}
+
+static gint
+get_image_meta_data_size (Image *image)
+{
+ gint i;
+
+ /* The complication with storing the size in both
+ * IconData and Image is necessary since we attribute
+ * the size of the IconData only to the first Image
+ * using it (at which time it is written out in the
+ * cache). Later Images just refer to the written out
+ * IconData via the offset.
+ */
+ if (image->icon_data_size == 0)
+ {
+ if (image->icon_data && image->icon_data->size < 0)
+ {
+ IconData *data = image->icon_data;
+
+ data->size = 0;
+
+ if (data->has_embedded_rect ||
+ data->n_attach_points > 0 ||
+ data->n_display_names > 0)
+ data->size += 12;
+
+ if (data->has_embedded_rect)
+ data->size += 8;
+
+ if (data->n_attach_points > 0)
+ data->size += 4 + data->n_attach_points * 4;
+
+ if (data->n_display_names > 0)
+ {
+ data->size += 4 + 8 * data->n_display_names;
+
+ for (i = 0; data->display_names[i]; i++)
+ {
+ int poolv;
+ if ((poolv = find_string (data->display_names[i])) == 0)
+ {
+ data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
+ /* Adding the string to the pool with -1
+ * to indicate that it hasn't been written out
+ * to the cache yet. We still need it in the
+ * pool in case the same string occurs twice
+ * during a get_single_node_size() calculation.
+ */
+ add_string (data->display_names[i], -1);
+ }
+ }
+ }
+
+ image->icon_data_size = data->size;
+ data->size = 0;
+ }
+ }
+
+ g_assert (image->icon_data_size % 4 == 0);
+
+ return image->icon_data_size;
+}
+
+static gint
+get_image_pixel_data_size (Image *image)
+{
+ /* The complication with storing the size in both
+ * ImageData and Image is necessary since we attribute
+ * the size of the ImageData only to the first Image
+ * using it (at which time it is written out in the
+ * cache). Later Images just refer to the written out
+ * ImageData via the offset.
+ */
+ if (image->pixel_data_size == 0)
+ {
+ if (image->image_data &&
+ image->image_data->has_pixdata)
+ {
+ image->pixel_data_size = image->image_data->size;
+ image->image_data->size = 0;
+ }
+ }
+
+ g_assert (image->pixel_data_size % 4 == 0);
+
+ return image->pixel_data_size;
+}
+
+static gint
+get_image_data_size (Image *image)
+{
+ gint len;
+
+ len = 0;
+
+ len += get_image_pixel_data_size (image);
+ len += get_image_meta_data_size (image);
+
+ /* Even if len is zero, we need to reserve space to
+ * write the ImageData, unless this is an .svg without
+ * .icon, in which case both image_data and icon_data
+ * are NULL.
+ */
+ if (len > 0 || image->image_data || image->icon_data)
+ len += 8;
+
+ return len;
+}
+
+static void
+get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
+{
+ GList *list;
+
+ /* Node pointers */
+ *node_size = 12;
+
+ /* Name */
+ if (find_string (node->name) == 0)
+ {
+ *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
+ add_string (node->name, -1);
+ }
+
+ /* Image list */
+ *node_size += 4 + g_list_length (node->image_list) * 8;
+
+ /* Image data */
+ *image_data_size = 0;
+ for (list = node->image_list; list; list = list->next)
+ {
+ Image *image = list->data;
+
+ *image_data_size += get_image_data_size (image);
+ }
+}
+
+static gboolean
+write_bucket (FILE *cache, HashNode *node, int *offset)
+{
+ while (node != NULL)
+ {
+ int node_size, image_data_size;
+ int next_offset, image_data_offset;
+ int data_offset;
+ int name_offset;
+ int name_size;
+ int image_list_offset;
+ int i, len;
+ GList *list;
+
+ g_assert (*offset == ftell (cache));
+
+ node->offset = *offset;
+
+ get_single_node_size (node, &node_size, &image_data_size);
+ g_assert (node_size % 4 == 0);
+ g_assert (image_data_size % 4 == 0);
+ image_data_offset = *offset + node_size;
+ next_offset = *offset + node_size + image_data_size;
+ /* Chain offset */
+ if (node->next != NULL)
+ {
+ if (!write_card32 (cache, next_offset))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0xffffffff))
+ return FALSE;
+ }
+
+ name_size = 0;
+ name_offset = find_string (node->name);
+ if (name_offset <= 0)
+ {
+ name_offset = *offset + 12;
+ name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
+ add_string (node->name, name_offset);
+ }
+ if (!write_card32 (cache, name_offset))
+ return FALSE;
+
+ image_list_offset = *offset + 12 + name_size;
+ if (!write_card32 (cache, image_list_offset))
+ return FALSE;
+
+ /* Icon name */
+ if (name_size > 0)
+ {
+ if (!write_string (cache, node->name))
+ return FALSE;
+ }
+
+ /* Image list */
+ len = g_list_length (node->image_list);
+ if (!write_card32 (cache, len))
+ return FALSE;
+
+ list = node->image_list;
+ data_offset = image_data_offset;
+ for (i = 0; i < len; i++)
+ {
+ Image *image = list->data;
+ int image_size = get_image_data_size (image);
+
+ /* Directory index */
+ if (!write_card16 (cache, image->dir_index))
+ return FALSE;
+
+ /* Flags */
+ if (!write_card16 (cache, image->flags))
+ return FALSE;
+
+ /* Image data offset */
+ if (image_size > 0)
+ {
+ if (!write_card32 (cache, data_offset))
+ return FALSE;
+ data_offset += image_size;
+ }
+ else
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ list = list->next;
+ }
+
+ /* Now write the image data */
+ list = node->image_list;
+ for (i = 0; i < len; i++, list = list->next)
+ {
+ Image *image = list->data;
+ int pixel_data_size = get_image_pixel_data_size (image);
+ int meta_data_size = get_image_meta_data_size (image);
+
+ if (get_image_data_size (image) == 0)
+ continue;
+
+ /* Pixel data */
+ if (pixel_data_size > 0)
+ {
+ image->image_data->offset = image_data_offset + 8;
+ if (!write_card32 (cache, image->image_data->offset))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
+ return FALSE;
+ }
+
+ if (meta_data_size > 0)
+ {
+ image->icon_data->offset = image_data_offset + pixel_data_size + 8;
+ if (!write_card32 (cache, image->icon_data->offset))
+ return FALSE;
+ }
+ else
+ {
+ if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
+ return FALSE;
+ }
+
+ if (pixel_data_size > 0)
+ {
+ if (!write_image_data (cache, image->image_data, image->image_data->offset))
+ return FALSE;
+ }
+
+ if (meta_data_size > 0)
+ {
+ if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
+ return FALSE;
+ }
+
+ image_data_offset += pixel_data_size + meta_data_size + 8;
+ }
+
+ *offset = next_offset;
+ node = node->next;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_hash_table (FILE *cache, HashContext *context, int *new_offset)
+{
+ int offset = HASH_OFFSET;
+ int node_offset;
+ int i;
+
+ if (!(write_card32 (cache, context->size)))
+ return FALSE;
+
+ offset += 4;
+ node_offset = offset + context->size * 4;
+ /* Just write zeros here, we will rewrite this later */
+ for (i = 0; i < context->size; i++)
+ {
+ if (!write_card32 (cache, 0))
+ return FALSE;
+ }
+
+ /* Now write the buckets */
+ for (i = 0; i < context->size; i++)
+ {
+ if (!context->nodes[i])
+ continue;
+
+ g_assert (node_offset % 4 == 0);
+ if (!write_bucket (cache, context->nodes[i], &node_offset))
+ return FALSE;
+ }
+
+ *new_offset = node_offset;
+
+ /* Now write out the bucket offsets */
+
+ fseek (cache, offset, SEEK_SET);
+
+ for (i = 0; i < context->size; i++)
+ {
+ if (context->nodes[i] != NULL)
+ node_offset = context->nodes[i]->offset;
+ else
+ node_offset = 0xffffffff;
+ if (!write_card32 (cache, node_offset))
+ return FALSE;
+ }
+
+ fseek (cache, 0, SEEK_END);
+
+ return TRUE;
+}
+
+static gboolean
+write_dir_index (FILE *cache, int offset, GList *directories)
+{
+ int n_dirs;
+ GList *d;
+ char *dir;
+ int tmp, tmp2;
+
+ n_dirs = g_list_length (directories);
+
+ if (!write_card32 (cache, n_dirs))
+ return FALSE;
+
+ offset += 4 + n_dirs * 4;
+
+ tmp = offset;
+ for (d = directories; d; d = d->next)
+ {
+ dir = d->data;
+
+ tmp2 = find_string (dir);
+
+ if (tmp2 == 0 || tmp2 == -1)
+ {
+ tmp2 = tmp;
+ tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
+ /* We're playing a little game with negative
+ * offsets here to handle duplicate strings in
+ * the array, even though that should not
+ * really happen for the directory index.
+ */
+ add_string (dir, -tmp2);
+ }
+ else if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ }
+
+ if (!write_card32 (cache, tmp2))
+ return FALSE;
+ }
+
+ g_assert (offset == ftell (cache));
+ for (d = directories; d; d = d->next)
+ {
+ dir = d->data;
+
+ tmp2 = find_string (dir);
+ g_assert (tmp2 != 0 && tmp2 != -1);
+ if (tmp2 < 0)
+ {
+ tmp2 = -tmp2;
+ g_assert (tmp2 == ftell (cache));
+ add_string (dir, tmp2);
+ if (!write_string (cache, dir))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_file (FILE *cache, GHashTable *files, GList *directories)
+{
+ HashContext context;
+ int new_offset;
+
+ /* Convert the hash table into something looking a bit more
+ * like what we want to write to disk.
+ */
+ context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
+ context.nodes = g_new0 (HashNode *, context.size);
+
+ g_hash_table_foreach_remove (files, convert_to_hash, &context);
+
+ /* Now write the file */
+ /* We write 0 as the directory list offset and go
+ * back and change it later */
+ if (!write_header (cache, 0))
+ {
+ g_printerr (_("Failed to write header\n"));
+ return FALSE;
+ }
+
+ if (!write_hash_table (cache, &context, &new_offset))
+ {
+ g_printerr (_("Failed to write hash table\n"));
+ return FALSE;
+ }
+
+ if (!write_dir_index (cache, new_offset, directories))
+ {
+ g_printerr (_("Failed to write folder index\n"));
+ return FALSE;
+ }
+
+ rewind (cache);
+
+ if (!write_header (cache, new_offset))
+ {
+ g_printerr (_("Failed to rewrite header\n"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+validate_file (const gchar *file)
+{
+ GMappedFile *map;
+ CacheInfo info;
+
+ map = g_mapped_file_new (file, FALSE, NULL);
+ if (!map)
+ return FALSE;
+
+ info.cache = g_mapped_file_get_contents (map);
+ info.cache_size = g_mapped_file_get_length (map);
+ info.n_directories = 0;
+ info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
+
+ if (!gtk_icon_cache_validate (&info))
+ {
+ g_mapped_file_unref (map);
+ return FALSE;
+ }
+
+ g_mapped_file_unref (map);
+
+ return TRUE;
+}
+
+/**
+ * safe_fclose:
+ * @f: A FILE* stream, must have underlying fd
+ *
+ * Unix defaults for data preservation after system crash
+ * are unspecified, and many systems will eat your data
+ * in this situation unless you explicitly fsync().
+ *
+ * Returns: %TRUE on success, %FALSE on failure, and will set errno()
+ */
+static gboolean
+safe_fclose (FILE *f)
+{
+ int fd = fileno (f);
+ g_assert (fd >= 0);
+ if (fflush (f) == EOF)
+ return FALSE;
+#ifndef G_OS_WIN32
+ if (fsync (fd) < 0)
+ return FALSE;
+#endif
+ if (fclose (f) == EOF)
+ return FALSE;
+ return TRUE;
+}
+
+static void
+build_cache (const gchar *path)
+{
+ gchar *cache_path, *tmp_cache_path;
+#ifdef G_OS_WIN32
+ gchar *bak_cache_path = NULL;
+#endif
+ GHashTable *files;
+ FILE *cache;
+ GStatBuf path_stat, cache_stat;
+ struct utimbuf utime_buf;
+ GList *directories = NULL;
+ int fd;
+ int retry_count = 0;
+#ifndef G_OS_WIN32
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+#else
+ int mode = _S_IWRITE | _S_IREAD;
+#endif
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+ tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+
+opentmp:
+ if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
+ {
+ if (retry_count == 0)
+ {
+ retry_count++;
+ g_remove (tmp_cache_path);
+ goto opentmp;
+ }
+ g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
+ exit (1);
+ }
+
+ cache = fdopen (fd, "wb");
+
+ if (!cache)
+ {
+ g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
+ exit (1);
+ }
+
+ files = g_hash_table_new (g_str_hash, g_str_equal);
+ image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ string_pool = g_hash_table_new (g_str_hash, g_str_equal);
+
+ directories = scan_directory (path, NULL, files, NULL, 0);
+
+ if (g_hash_table_size (files) == 0)
+ {
+ /* Empty table, just close and remove the file */
+
+ fclose (cache);
+ g_unlink (tmp_cache_path);
+ g_unlink (cache_path);
+ exit (0);
+ }
+
+ /* FIXME: Handle failure */
+ if (!write_file (cache, files, directories))
+ {
+ g_unlink (tmp_cache_path);
+ exit (1);
+ }
+
+ if (!safe_fclose (cache))
+ {
+ g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
+ g_unlink (tmp_cache_path);
+ exit (1);
+ }
+ cache = NULL;
+
+ g_list_free_full (directories, g_free);
+
+ if (!validate_file (tmp_cache_path))
+ {
+ g_printerr (_("The generated cache was invalid.\n"));
+ /*g_unlink (tmp_cache_path);*/
+ exit (1);
+ }
+
+#ifdef G_OS_WIN32
+ if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
+ {
+ bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
+ g_unlink (bak_cache_path);
+ if (g_rename (cache_path, bak_cache_path) == -1)
+ {
+ int errsv = errno;
+
+ g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
+ cache_path, bak_cache_path,
+ g_strerror (errsv),
+ cache_path);
+ g_unlink (cache_path);
+ bak_cache_path = NULL;
+ }
+ }
+#endif
+
+ if (g_rename (tmp_cache_path, cache_path) == -1)
+ {
+ int errsv = errno;
+
+ g_printerr (_("Could not rename %s to %s: %s\n"),
+ tmp_cache_path, cache_path,
+ g_strerror (errsv));
+ g_unlink (tmp_cache_path);
+#ifdef G_OS_WIN32
+ if (bak_cache_path != NULL)
+ if (g_rename (bak_cache_path, cache_path) == -1)
+ {
+ errsv = errno;
+
+ g_printerr (_("Could not rename %s back to %s: %s.\n"),
+ bak_cache_path, cache_path,
+ g_strerror (errsv));
+ }
+#endif
+ exit (1);
+ }
+#ifdef G_OS_WIN32
+ if (bak_cache_path != NULL)
+ g_unlink (bak_cache_path);
+#endif
+
+ /* Update time */
+ /* FIXME: What do do if an error occurs here? */
+ if (g_stat (path, &path_stat) < 0 ||
+ g_stat (cache_path, &cache_stat))
+ exit (1);
+
+ utime_buf.actime = path_stat.st_atime;
+ utime_buf.modtime = cache_stat.st_mtime;
+#if GLIB_CHECK_VERSION (2, 17, 1)
+ g_utime (path, &utime_buf);
+#else
+ utime (path, &utime_buf);
+#endif
+
+ if (!quiet)
+ g_printerr (_("Cache file created successfully.\n"));
+}
+
+static void
+write_csource (const gchar *path)
+{
+ gchar *cache_path;
+ gchar *data;
+ gsize len;
+ gint i;
+
+ cache_path = g_build_filename (path, CACHE_NAME, NULL);
+ if (!g_file_get_contents (cache_path, &data, &len, NULL))
+ exit (1);
+
+ g_printf ("#ifdef __SUNPRO_C\n");
+ g_printf ("#pragma align 4 (%s)\n", var_name);
+ g_printf ("#endif\n");
+
+ g_printf ("#ifdef __GNUC__\n");
+ g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
+ g_printf ("#else\n");
+ g_printf ("static const guint8 %s[] = \n", var_name);
+ g_printf ("#endif\n");
+
+ g_printf ("{\n");
+ for (i = 0; i < len - 1; i++)
+ {
+ if (i %12 == 0)
+ g_printf (" ");
+ g_printf ("0x%02x, ", (guint8)data[i]);
+ if (i % 12 == 11)
+ g_printf ("\n");
+ }
+
+ g_printf ("0x%02x\n};\n", (guint8)data[i]);
+}
+
+static GOptionEntry args[] = {
+ { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
+ { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don’t check for the existence of index.theme"), NULL },
+ { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don’t include image data in the cache"), NULL },
+ { "include-image-data", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &index_only, N_("Include image data in the cache"), NULL },
+ { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
+ { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
+ { NULL }
+};
+
+static void
+printerr_handler (const gchar *string)
+{
+ const gchar *charset;
+
+ fputs (g_get_prgname (), stderr);
+ fputs (": ", stderr);
+ if (g_get_charset (&charset))
+ fputs (string, stderr); /* charset is UTF-8 already */
+ else
+ {
+ gchar *result;
+
+ result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
+
+ if (result)
+ {
+ fputs (result, stderr);
+ g_free (result);
+ }
+
+ fflush (stderr);
+ }
+}
+
+
+int
+main (int argc, char **argv)
+{
+ gchar *path;
+ GOptionContext *context;
+
+ if (argc < 2)
+ return 0;
+
+ g_set_printerr_handler (printerr_handler);
+
+ setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+ context = g_option_context_new ("ICONPATH");
+ g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
+
+ g_option_context_parse (context, &argc, &argv, NULL);
+
+ path = argv[1];
+#ifdef G_OS_WIN32
+ path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
+#endif
+
+ if (validate)
+ {
+ gchar *file = g_build_filename (path, CACHE_NAME, NULL);
+
+ if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
+ {
+ if (!quiet)
+ g_printerr (_("File not found: %s\n"), file);
+ exit (1);
+ }
+ if (!validate_file (file))
+ {
+ if (!quiet)
+ g_printerr (_("Not a valid icon cache: %s\n"), file);
+ exit (1);
+ }
+ else
+ {
+ exit (0);
+ }
+ }
+
+ if (!ignore_theme_index && !has_theme_index (path))
+ {
+ if (path)
+ {
+ g_printerr (_("No theme index file.\n"));
+ }
+ else
+ {
+ g_printerr (_("No theme index file in “%s”.\n"
+ "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
+ }
+
+ return 1;
+ }
+
+ if (!force_update && is_cache_up_to_date (path))
+ return 0;
+
+ replace_backslashes_with_slashes (path);
+ build_cache (path);
+
+ if (strcmp (var_name, "-") != 0)
+ write_csource (path);
+
+ return 0;
+}
+++ /dev/null
-/* updateiconcache.c
- * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <locale.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <errno.h>
-#ifdef _MSC_VER
-#include <io.h>
-#include <sys/utime.h>
-#else
-#include <utime.h>
-#endif
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <gdk-pixbuf/gdk-pixdata.h>
-#include <glib/gi18n.h>
-#include "gtkiconcachevalidatorprivate.h"
-
-static gboolean force_update = FALSE;
-static gboolean ignore_theme_index = FALSE;
-static gboolean quiet = FALSE;
-static gboolean index_only = TRUE;
-static gboolean validate = FALSE;
-static gchar *var_name = (gchar *) "-";
-
-#define CACHE_NAME "icon-theme.cache"
-
-#define HAS_SUFFIX_XPM (1 << 0)
-#define HAS_SUFFIX_SVG (1 << 1)
-#define HAS_SUFFIX_PNG (1 << 2)
-#define HAS_ICON_FILE (1 << 3)
-
-#define MAJOR_VERSION 1
-#define MINOR_VERSION 0
-#define HASH_OFFSET 12
-
-#define ALIGN_VALUE(this, boundary) \
- (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
-
-#ifdef HAVE_FTW_H
-
-#include <ftw.h>
-
-static GStatBuf cache_dir_stat;
-static gboolean cache_up_to_date;
-
-static int check_dir_mtime (const char *dir,
- const struct stat *sb,
- int tf)
-{
- if (tf != FTW_NS && sb->st_mtime > cache_dir_stat.st_mtime)
- {
- cache_up_to_date = FALSE;
- /* stop tree walk */
- return 1;
- }
-
- return 0;
-}
-
-static gboolean
-is_cache_up_to_date (const gchar *path)
-{
- gchar *cache_path;
- gint retval;
-
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
- retval = g_stat (cache_path, &cache_dir_stat);
- g_free (cache_path);
-
- if (retval < 0)
- {
- /* Cache file not found */
- return FALSE;
- }
-
- cache_up_to_date = TRUE;
-
- ftw (path, check_dir_mtime, 20);
-
- return cache_up_to_date;
-}
-
-#else /* !HAVE_FTW_H */
-
-gboolean
-is_cache_up_to_date (const gchar *path)
-{
- GStatBuf path_stat, cache_stat;
- gchar *cache_path;
- int retval;
-
- retval = g_stat (path, &path_stat);
-
- if (retval < 0)
- {
- /* We can't stat the path,
- * assume we have a updated cache */
- return TRUE;
- }
-
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
- retval = g_stat (cache_path, &cache_stat);
- g_free (cache_path);
-
- if (retval < 0)
- {
- /* Cache file not found */
- return FALSE;
- }
-
- /* Check mtime */
- return cache_stat.st_mtime >= path_stat.st_mtime;
-}
-
-#endif /* !HAVE_FTW_H */
-
-static gboolean
-has_theme_index (const gchar *path)
-{
- gboolean result;
- gchar *index_path;
-
- index_path = g_build_filename (path, "index.theme", NULL);
-
- result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
-
- g_free (index_path);
-
- return result;
-}
-
-
-typedef struct
-{
- GdkPixdata pixdata;
- gboolean has_pixdata;
- guint32 offset;
- guint size;
-} ImageData;
-
-typedef struct
-{
- int has_embedded_rect;
- int x0, y0, x1, y1;
-
- int n_attach_points;
- int *attach_points;
-
- int n_display_names;
- char **display_names;
-
- guint32 offset;
- gint size;
-} IconData;
-
-static GHashTable *image_data_hash = NULL;
-static GHashTable *icon_data_hash = NULL;
-
-typedef struct
-{
- int flags;
- int dir_index;
-
- ImageData *image_data;
- guint pixel_data_size;
-
- IconData *icon_data;
- guint icon_data_size;
-} Image;
-
-
-static gboolean
-foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
-{
- Image *image = (Image *)value;
- GHashTable *files = user_data;
- GList *list;
- gboolean free_key = FALSE;
-
- if (image->flags == HAS_ICON_FILE)
- {
- /* just a .icon file, throw away */
- g_free (key);
- g_free (image);
-
- return TRUE;
- }
-
- list = g_hash_table_lookup (files, key);
- if (list)
- free_key = TRUE;
-
- list = g_list_prepend (list, value);
- g_hash_table_insert (files, key, list);
-
- if (free_key)
- g_free (key);
-
- return TRUE;
-}
-
-static IconData *
-load_icon_data (const char *path)
-{
- GKeyFile *icon_file;
- char **split;
- gsize length;
- char *str;
- char *split_point;
- int i;
- gint *ivalues;
- GError *error = NULL;
- gchar **keys;
- gsize n_keys;
- IconData *data;
-
- icon_file = g_key_file_new ();
- g_key_file_set_list_separator (icon_file, ',');
- g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
- if (error)
- {
- g_error_free (error);
- g_key_file_free (icon_file);
-
- return NULL;
- }
-
- data = g_new0 (IconData, 1);
-
- ivalues = g_key_file_get_integer_list (icon_file,
- "Icon Data", "EmbeddedTextRectangle",
- &length, NULL);
- if (ivalues)
- {
- if (length == 4)
- {
- data->has_embedded_rect = TRUE;
- data->x0 = ivalues[0];
- data->y0 = ivalues[1];
- data->x1 = ivalues[2];
- data->y1 = ivalues[3];
- }
-
- g_free (ivalues);
- }
-
- str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
- if (str)
- {
- split = g_strsplit (str, "|", -1);
-
- data->n_attach_points = g_strv_length (split);
- data->attach_points = g_new (int, 2 * data->n_attach_points);
-
- for (i = 0; i < data->n_attach_points; ++i)
- {
- split_point = strchr (split[i], ',');
- if (split_point)
- {
- *split_point = 0;
- split_point++;
- data->attach_points[2 * i] = atoi (split[i]);
- data->attach_points[2 * i + 1] = atoi (split_point);
- }
- }
-
- g_strfreev (split);
- g_free (str);
- }
-
- keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
- data->display_names = g_new0 (gchar *, 2 * n_keys + 1);
- data->n_display_names = 0;
-
- for (i = 0; i < n_keys; i++)
- {
- gchar *lang, *name;
-
- if (g_str_has_prefix (keys[i], "DisplayName"))
- {
- gchar *open, *close = NULL;
-
- open = strchr (keys[i], '[');
-
- if (open)
- close = strchr (open, ']');
-
- if (open && close)
- {
- lang = g_strndup (open + 1, close - open - 1);
- name = g_key_file_get_locale_string (icon_file,
- "Icon Data", "DisplayName",
- lang, NULL);
- }
- else
- {
- lang = g_strdup ("C");
- name = g_key_file_get_string (icon_file,
- "Icon Data", "DisplayName",
- NULL);
- }
-
- data->display_names[2 * data->n_display_names] = lang;
- data->display_names[2 * data->n_display_names + 1] = name;
- data->n_display_names++;
- }
- }
-
- g_strfreev (keys);
-
- g_key_file_free (icon_file);
-
- /* -1 means not computed yet, the real value depends
- * on string pool state, and will be computed
- * later
- */
- data->size = -1;
-
- return data;
-}
-
-/*
- * This function was copied from gtkfilesystemunix.c, it should
- * probably go to GLib
- */
-static void
-canonicalize_filename (gchar *filename)
-{
- gchar *p, *q;
- gboolean last_was_slash = FALSE;
-
- p = filename;
- q = filename;
-
- while (*p)
- {
- if (*p == G_DIR_SEPARATOR)
- {
- if (!last_was_slash)
- *q++ = G_DIR_SEPARATOR;
-
- last_was_slash = TRUE;
- }
- else
- {
- if (last_was_slash && *p == '.')
- {
- if (*(p + 1) == G_DIR_SEPARATOR ||
- *(p + 1) == '\0')
- {
- if (*(p + 1) == '\0')
- break;
-
- p += 1;
- }
- else if (*(p + 1) == '.' &&
- (*(p + 2) == G_DIR_SEPARATOR ||
- *(p + 2) == '\0'))
- {
- if (q > filename + 1)
- {
- q--;
- while (q > filename + 1 &&
- *(q - 1) != G_DIR_SEPARATOR)
- q--;
- }
-
- if (*(p + 2) == '\0')
- break;
-
- p += 2;
- }
- else
- {
- *q++ = *p;
- last_was_slash = FALSE;
- }
- }
- else
- {
- *q++ = *p;
- last_was_slash = FALSE;
- }
- }
-
- p++;
- }
-
- if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
- q--;
-
- *q = '\0';
-}
-
-static gchar *
-follow_links (const gchar *path)
-{
- gchar *target;
- gchar *d, *s;
- gchar *path2 = NULL;
-
- path2 = g_strdup (path);
- while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
- {
- target = g_file_read_link (path2, NULL);
-
- if (target)
- {
- if (g_path_is_absolute (target))
- path2 = target;
- else
- {
- d = g_path_get_dirname (path2);
- s = g_build_filename (d, target, NULL);
- g_free (d);
- g_free (target);
- g_free (path2);
- path2 = s;
- }
- }
- else
- break;
- }
-
- if (strcmp (path, path2) == 0)
- {
- g_free (path2);
- path2 = NULL;
- }
-
- return path2;
-}
-
-static void
-maybe_cache_image_data (Image *image,
- const gchar *path)
-{
- if (!index_only && !image->image_data &&
- (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
- {
- GdkPixbuf *pixbuf;
- ImageData *idata;
- gchar *path2;
-
- idata = g_hash_table_lookup (image_data_hash, path);
- path2 = follow_links (path);
-
- if (path2)
- {
- ImageData *idata2;
-
- canonicalize_filename (path2);
-
- idata2 = g_hash_table_lookup (image_data_hash, path2);
-
- if (idata && idata2 && idata != idata2)
- g_error ("different idatas found for symlinked '%s' and '%s'\n",
- path, path2);
-
- if (idata && !idata2)
- g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
-
- if (!idata && idata2)
- {
- g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
- idata = idata2;
- }
- }
-
- if (!idata)
- {
- idata = g_new0 (ImageData, 1);
- g_hash_table_insert (image_data_hash, g_strdup (path), idata);
- if (path2)
- g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
- }
-
- if (!idata->has_pixdata)
- {
- pixbuf = gdk_pixbuf_new_from_file (path, NULL);
-
- if (pixbuf)
- {
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
- gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
-G_GNUC_END_IGNORE_DEPRECATIONS;
- idata->size = idata->pixdata.length + 8;
- idata->has_pixdata = TRUE;
- }
- }
-
- image->image_data = idata;
-
- g_free (path2);
- }
-}
-
-static void
-maybe_cache_icon_data (Image *image,
- const gchar *path)
-{
- if (g_str_has_suffix (path, ".icon"))
- {
- IconData *idata = NULL;
- gchar *path2 = NULL;
-
- idata = g_hash_table_lookup (icon_data_hash, path);
- path2 = follow_links (path);
-
- if (path2)
- {
- IconData *idata2;
-
- canonicalize_filename (path2);
-
- idata2 = g_hash_table_lookup (icon_data_hash, path2);
-
- if (idata && idata2 && idata != idata2)
- g_error ("different idatas found for symlinked '%s' and '%s'\n",
- path, path2);
-
- if (idata && !idata2)
- g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
-
- if (!idata && idata2)
- {
- g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
- idata = idata2;
- }
- }
-
- if (!idata)
- {
- idata = load_icon_data (path);
- g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
- if (path2)
- g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
- }
-
- image->icon_data = idata;
-
- g_free (path2);
- }
-}
-
-/*
- * Finds all dir separators and replaces them with “/”.
- * This makes sure that only /-separated paths are written in cache files,
- * maintaining compatibility with theme index files that use slashes as
- * directory separators on all platforms.
- */
-static void
-replace_backslashes_with_slashes (gchar *path)
-{
- size_t i;
- if (path == NULL)
- return;
- for (i = 0; path[i]; i++)
- if (G_IS_DIR_SEPARATOR (path[i]))
- path[i] = '/';
-}
-
-static GList *
-scan_directory (const gchar *base_path,
- const gchar *subdir,
- GHashTable *files,
- GList *directories,
- gint depth)
-{
- GHashTable *dir_hash;
- GDir *dir;
- GList *list = NULL, *iterator = NULL;
- const gchar *name;
- gchar *dir_path;
- gboolean dir_added = FALSE;
- guint dir_index = 0xffff;
-
- dir_path = g_build_path ("/", base_path, subdir, NULL);
-
- /* FIXME: Use the gerror */
- dir = g_dir_open (dir_path, 0, NULL);
-
- if (!dir)
- return directories;
-
- dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
-
- while ((name = g_dir_read_name (dir)))
- {
- list = g_list_prepend (list, g_strdup (name));
- }
- list = g_list_sort (list, (GCompareFunc) strcmp);
- for (iterator = list; iterator; iterator = iterator->next)
- {
- name = iterator->data;
-
- gchar *path;
- gboolean retval;
- int flags = 0;
- Image *image;
- gchar *basename, *dot;
-
- path = g_build_filename (dir_path, name, NULL);
-
- retval = g_file_test (path, G_FILE_TEST_IS_DIR);
- if (retval)
- {
- gchar *subsubdir;
-
- if (subdir)
- subsubdir = g_build_path ("/", subdir, name, NULL);
- else
- subsubdir = g_strdup (name);
- directories = scan_directory (base_path, subsubdir, files,
- directories, depth + 1);
- g_free (subsubdir);
-
- continue;
- }
-
- /* ignore images in the toplevel directory */
- if (subdir == NULL)
- continue;
-
- retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
- if (retval)
- {
- if (g_str_has_suffix (name, ".png"))
- flags |= HAS_SUFFIX_PNG;
- else if (g_str_has_suffix (name, ".svg"))
- flags |= HAS_SUFFIX_SVG;
- else if (g_str_has_suffix (name, ".xpm"))
- flags |= HAS_SUFFIX_XPM;
- else if (g_str_has_suffix (name, ".icon"))
- flags |= HAS_ICON_FILE;
-
- if (flags == 0)
- continue;
-
- basename = g_strdup (name);
- dot = strrchr (basename, '.');
- *dot = '\0';
-
- image = g_hash_table_lookup (dir_hash, basename);
- if (!image)
- {
- if (!dir_added)
- {
- dir_added = TRUE;
- if (subdir)
- {
- dir_index = g_list_length (directories);
- directories = g_list_append (directories, g_strdup (subdir));
- }
- else
- dir_index = 0xffff;
- }
-
- image = g_new0 (Image, 1);
- image->dir_index = dir_index;
- g_hash_table_insert (dir_hash, g_strdup (basename), image);
- }
-
- image->flags |= flags;
-
- maybe_cache_image_data (image, path);
- maybe_cache_icon_data (image, path);
-
- g_free (basename);
- }
-
- g_free (path);
- }
-
- g_list_free_full (list, g_free);
- g_dir_close (dir);
-
- /* Move dir into the big file hash */
- g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
-
- g_hash_table_destroy (dir_hash);
-
- return directories;
-}
-
-typedef struct _HashNode HashNode;
-
-struct _HashNode
-{
- HashNode *next;
- gchar *name;
- GList *image_list;
- gint offset;
-};
-
-static guint
-icon_name_hash (gconstpointer key)
-{
- const signed char *p = key;
- guint32 h = *p;
-
- if (h)
- for (p += 1; *p != '\0'; p++)
- h = (h << 5) - h + *p;
-
- return h;
-}
-
-typedef struct {
- gint size;
- HashNode **nodes;
-} HashContext;
-
-static gboolean
-convert_to_hash (gpointer key, gpointer value, gpointer user_data)
-{
- HashContext *context = user_data;
- guint hash;
- HashNode *node;
-
- hash = icon_name_hash (key) % context->size;
-
- node = g_new0 (HashNode, 1);
- node->next = NULL;
- node->name = key;
- node->image_list = value;
-
- if (context->nodes[hash] != NULL)
- node->next = context->nodes[hash];
-
- context->nodes[hash] = node;
-
- return TRUE;
-}
-
-static GHashTable *string_pool = NULL;
-
-static int
-find_string (const gchar *n)
-{
- return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
-}
-
-static void
-add_string (const gchar *n, int offset)
-{
- g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
-}
-
-static gboolean
-write_string (FILE *cache, const gchar *n)
-{
- gchar *s;
- int i, l;
-
- l = ALIGN_VALUE (strlen (n) + 1, 4);
-
- s = g_malloc0 (l);
- strcpy (s, n);
-
- i = fwrite (s, l, 1, cache);
-
- g_free (s);
-
- return i == 1;
-
-}
-
-static gboolean
-write_card16 (FILE *cache, guint16 n)
-{
- int i;
-
- n = GUINT16_TO_BE (n);
-
- i = fwrite ((char *)&n, 2, 1, cache);
-
- return i == 1;
-}
-
-static gboolean
-write_card32 (FILE *cache, guint32 n)
-{
- int i;
-
- n = GUINT32_TO_BE (n);
-
- i = fwrite ((char *)&n, 4, 1, cache);
-
- return i == 1;
-}
-
-
-static gboolean
-write_image_data (FILE *cache, ImageData *image_data, int offset)
-{
- guint8 *s;
- guint len;
- gint i;
- GdkPixdata *pixdata = &image_data->pixdata;
-
- /* Type 0 is GdkPixdata */
- if (!write_card32 (cache, 0))
- return FALSE;
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
- s = gdk_pixdata_serialize (pixdata, &len);
-G_GNUC_END_IGNORE_DEPRECATIONS;
-
- if (!write_card32 (cache, len))
- {
- g_free (s);
- return FALSE;
- }
-
- i = fwrite (s, len, 1, cache);
-
- g_free (s);
-
- return i == 1;
-}
-
-static gboolean
-write_icon_data (FILE *cache, IconData *icon_data, int offset)
-{
- int ofs = offset + 12;
- int j;
- int tmp, tmp2;
-
- if (icon_data->has_embedded_rect)
- {
- if (!write_card32 (cache, ofs))
- return FALSE;
-
- ofs += 8;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- if (icon_data->n_attach_points > 0)
- {
- if (!write_card32 (cache, ofs))
- return FALSE;
-
- ofs += 4 + 4 * icon_data->n_attach_points;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- if (icon_data->n_display_names > 0)
- {
- if (!write_card32 (cache, ofs))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- if (icon_data->has_embedded_rect)
- {
- if (!write_card16 (cache, icon_data->x0) ||
- !write_card16 (cache, icon_data->y0) ||
- !write_card16 (cache, icon_data->x1) ||
- !write_card16 (cache, icon_data->y1))
- return FALSE;
- }
-
- if (icon_data->n_attach_points > 0)
- {
- if (!write_card32 (cache, icon_data->n_attach_points))
- return FALSE;
-
- for (j = 0; j < 2 * icon_data->n_attach_points; j++)
- {
- if (!write_card16 (cache, icon_data->attach_points[j]))
- return FALSE;
- }
- }
-
- if (icon_data->n_display_names > 0)
- {
- if (!write_card32 (cache, icon_data->n_display_names))
- return FALSE;
-
- ofs += 4 + 8 * icon_data->n_display_names;
-
- tmp = ofs;
- for (j = 0; j < 2 * icon_data->n_display_names; j++)
- {
- tmp2 = find_string (icon_data->display_names[j]);
- if (tmp2 == 0 || tmp2 == -1)
- {
- tmp2 = tmp;
- tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
- /* We're playing a little game with negative
- * offsets here to handle duplicate strings in
- * the array.
- */
- add_string (icon_data->display_names[j], -tmp2);
- }
- else if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- }
-
- if (!write_card32 (cache, tmp2))
- return FALSE;
-
- }
-
- g_assert (ofs == ftell (cache));
- for (j = 0; j < 2 * icon_data->n_display_names; j++)
- {
- tmp2 = find_string (icon_data->display_names[j]);
- g_assert (tmp2 != 0 && tmp2 != -1);
- if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- g_assert (tmp2 == ftell (cache));
- add_string (icon_data->display_names[j], tmp2);
- if (!write_string (cache, icon_data->display_names[j]))
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-write_header (FILE *cache, guint32 dir_list_offset)
-{
- return (write_card16 (cache, MAJOR_VERSION) &&
- write_card16 (cache, MINOR_VERSION) &&
- write_card32 (cache, HASH_OFFSET) &&
- write_card32 (cache, dir_list_offset));
-}
-
-static gint
-get_image_meta_data_size (Image *image)
-{
- gint i;
-
- /* The complication with storing the size in both
- * IconData and Image is necessary since we attribute
- * the size of the IconData only to the first Image
- * using it (at which time it is written out in the
- * cache). Later Images just refer to the written out
- * IconData via the offset.
- */
- if (image->icon_data_size == 0)
- {
- if (image->icon_data && image->icon_data->size < 0)
- {
- IconData *data = image->icon_data;
-
- data->size = 0;
-
- if (data->has_embedded_rect ||
- data->n_attach_points > 0 ||
- data->n_display_names > 0)
- data->size += 12;
-
- if (data->has_embedded_rect)
- data->size += 8;
-
- if (data->n_attach_points > 0)
- data->size += 4 + data->n_attach_points * 4;
-
- if (data->n_display_names > 0)
- {
- data->size += 4 + 8 * data->n_display_names;
-
- for (i = 0; data->display_names[i]; i++)
- {
- int poolv;
- if ((poolv = find_string (data->display_names[i])) == 0)
- {
- data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
- /* Adding the string to the pool with -1
- * to indicate that it hasn't been written out
- * to the cache yet. We still need it in the
- * pool in case the same string occurs twice
- * during a get_single_node_size() calculation.
- */
- add_string (data->display_names[i], -1);
- }
- }
- }
-
- image->icon_data_size = data->size;
- data->size = 0;
- }
- }
-
- g_assert (image->icon_data_size % 4 == 0);
-
- return image->icon_data_size;
-}
-
-static gint
-get_image_pixel_data_size (Image *image)
-{
- /* The complication with storing the size in both
- * ImageData and Image is necessary since we attribute
- * the size of the ImageData only to the first Image
- * using it (at which time it is written out in the
- * cache). Later Images just refer to the written out
- * ImageData via the offset.
- */
- if (image->pixel_data_size == 0)
- {
- if (image->image_data &&
- image->image_data->has_pixdata)
- {
- image->pixel_data_size = image->image_data->size;
- image->image_data->size = 0;
- }
- }
-
- g_assert (image->pixel_data_size % 4 == 0);
-
- return image->pixel_data_size;
-}
-
-static gint
-get_image_data_size (Image *image)
-{
- gint len;
-
- len = 0;
-
- len += get_image_pixel_data_size (image);
- len += get_image_meta_data_size (image);
-
- /* Even if len is zero, we need to reserve space to
- * write the ImageData, unless this is an .svg without
- * .icon, in which case both image_data and icon_data
- * are NULL.
- */
- if (len > 0 || image->image_data || image->icon_data)
- len += 8;
-
- return len;
-}
-
-static void
-get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
-{
- GList *list;
-
- /* Node pointers */
- *node_size = 12;
-
- /* Name */
- if (find_string (node->name) == 0)
- {
- *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
- add_string (node->name, -1);
- }
-
- /* Image list */
- *node_size += 4 + g_list_length (node->image_list) * 8;
-
- /* Image data */
- *image_data_size = 0;
- for (list = node->image_list; list; list = list->next)
- {
- Image *image = list->data;
-
- *image_data_size += get_image_data_size (image);
- }
-}
-
-static gboolean
-write_bucket (FILE *cache, HashNode *node, int *offset)
-{
- while (node != NULL)
- {
- int node_size, image_data_size;
- int next_offset, image_data_offset;
- int data_offset;
- int name_offset;
- int name_size;
- int image_list_offset;
- int i, len;
- GList *list;
-
- g_assert (*offset == ftell (cache));
-
- node->offset = *offset;
-
- get_single_node_size (node, &node_size, &image_data_size);
- g_assert (node_size % 4 == 0);
- g_assert (image_data_size % 4 == 0);
- image_data_offset = *offset + node_size;
- next_offset = *offset + node_size + image_data_size;
- /* Chain offset */
- if (node->next != NULL)
- {
- if (!write_card32 (cache, next_offset))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, 0xffffffff))
- return FALSE;
- }
-
- name_size = 0;
- name_offset = find_string (node->name);
- if (name_offset <= 0)
- {
- name_offset = *offset + 12;
- name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
- add_string (node->name, name_offset);
- }
- if (!write_card32 (cache, name_offset))
- return FALSE;
-
- image_list_offset = *offset + 12 + name_size;
- if (!write_card32 (cache, image_list_offset))
- return FALSE;
-
- /* Icon name */
- if (name_size > 0)
- {
- if (!write_string (cache, node->name))
- return FALSE;
- }
-
- /* Image list */
- len = g_list_length (node->image_list);
- if (!write_card32 (cache, len))
- return FALSE;
-
- list = node->image_list;
- data_offset = image_data_offset;
- for (i = 0; i < len; i++)
- {
- Image *image = list->data;
- int image_size = get_image_data_size (image);
-
- /* Directory index */
- if (!write_card16 (cache, image->dir_index))
- return FALSE;
-
- /* Flags */
- if (!write_card16 (cache, image->flags))
- return FALSE;
-
- /* Image data offset */
- if (image_size > 0)
- {
- if (!write_card32 (cache, data_offset))
- return FALSE;
- data_offset += image_size;
- }
- else
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- list = list->next;
- }
-
- /* Now write the image data */
- list = node->image_list;
- for (i = 0; i < len; i++, list = list->next)
- {
- Image *image = list->data;
- int pixel_data_size = get_image_pixel_data_size (image);
- int meta_data_size = get_image_meta_data_size (image);
-
- if (get_image_data_size (image) == 0)
- continue;
-
- /* Pixel data */
- if (pixel_data_size > 0)
- {
- image->image_data->offset = image_data_offset + 8;
- if (!write_card32 (cache, image->image_data->offset))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
- return FALSE;
- }
-
- if (meta_data_size > 0)
- {
- image->icon_data->offset = image_data_offset + pixel_data_size + 8;
- if (!write_card32 (cache, image->icon_data->offset))
- return FALSE;
- }
- else
- {
- if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
- return FALSE;
- }
-
- if (pixel_data_size > 0)
- {
- if (!write_image_data (cache, image->image_data, image->image_data->offset))
- return FALSE;
- }
-
- if (meta_data_size > 0)
- {
- if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
- return FALSE;
- }
-
- image_data_offset += pixel_data_size + meta_data_size + 8;
- }
-
- *offset = next_offset;
- node = node->next;
- }
-
- return TRUE;
-}
-
-static gboolean
-write_hash_table (FILE *cache, HashContext *context, int *new_offset)
-{
- int offset = HASH_OFFSET;
- int node_offset;
- int i;
-
- if (!(write_card32 (cache, context->size)))
- return FALSE;
-
- offset += 4;
- node_offset = offset + context->size * 4;
- /* Just write zeros here, we will rewrite this later */
- for (i = 0; i < context->size; i++)
- {
- if (!write_card32 (cache, 0))
- return FALSE;
- }
-
- /* Now write the buckets */
- for (i = 0; i < context->size; i++)
- {
- if (!context->nodes[i])
- continue;
-
- g_assert (node_offset % 4 == 0);
- if (!write_bucket (cache, context->nodes[i], &node_offset))
- return FALSE;
- }
-
- *new_offset = node_offset;
-
- /* Now write out the bucket offsets */
-
- fseek (cache, offset, SEEK_SET);
-
- for (i = 0; i < context->size; i++)
- {
- if (context->nodes[i] != NULL)
- node_offset = context->nodes[i]->offset;
- else
- node_offset = 0xffffffff;
- if (!write_card32 (cache, node_offset))
- return FALSE;
- }
-
- fseek (cache, 0, SEEK_END);
-
- return TRUE;
-}
-
-static gboolean
-write_dir_index (FILE *cache, int offset, GList *directories)
-{
- int n_dirs;
- GList *d;
- char *dir;
- int tmp, tmp2;
-
- n_dirs = g_list_length (directories);
-
- if (!write_card32 (cache, n_dirs))
- return FALSE;
-
- offset += 4 + n_dirs * 4;
-
- tmp = offset;
- for (d = directories; d; d = d->next)
- {
- dir = d->data;
-
- tmp2 = find_string (dir);
-
- if (tmp2 == 0 || tmp2 == -1)
- {
- tmp2 = tmp;
- tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
- /* We're playing a little game with negative
- * offsets here to handle duplicate strings in
- * the array, even though that should not
- * really happen for the directory index.
- */
- add_string (dir, -tmp2);
- }
- else if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- }
-
- if (!write_card32 (cache, tmp2))
- return FALSE;
- }
-
- g_assert (offset == ftell (cache));
- for (d = directories; d; d = d->next)
- {
- dir = d->data;
-
- tmp2 = find_string (dir);
- g_assert (tmp2 != 0 && tmp2 != -1);
- if (tmp2 < 0)
- {
- tmp2 = -tmp2;
- g_assert (tmp2 == ftell (cache));
- add_string (dir, tmp2);
- if (!write_string (cache, dir))
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-write_file (FILE *cache, GHashTable *files, GList *directories)
-{
- HashContext context;
- int new_offset;
-
- /* Convert the hash table into something looking a bit more
- * like what we want to write to disk.
- */
- context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
- context.nodes = g_new0 (HashNode *, context.size);
-
- g_hash_table_foreach_remove (files, convert_to_hash, &context);
-
- /* Now write the file */
- /* We write 0 as the directory list offset and go
- * back and change it later */
- if (!write_header (cache, 0))
- {
- g_printerr (_("Failed to write header\n"));
- return FALSE;
- }
-
- if (!write_hash_table (cache, &context, &new_offset))
- {
- g_printerr (_("Failed to write hash table\n"));
- return FALSE;
- }
-
- if (!write_dir_index (cache, new_offset, directories))
- {
- g_printerr (_("Failed to write folder index\n"));
- return FALSE;
- }
-
- rewind (cache);
-
- if (!write_header (cache, new_offset))
- {
- g_printerr (_("Failed to rewrite header\n"));
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-validate_file (const gchar *file)
-{
- GMappedFile *map;
- CacheInfo info;
-
- map = g_mapped_file_new (file, FALSE, NULL);
- if (!map)
- return FALSE;
-
- info.cache = g_mapped_file_get_contents (map);
- info.cache_size = g_mapped_file_get_length (map);
- info.n_directories = 0;
- info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
-
- if (!gtk_icon_cache_validate (&info))
- {
- g_mapped_file_unref (map);
- return FALSE;
- }
-
- g_mapped_file_unref (map);
-
- return TRUE;
-}
-
-/**
- * safe_fclose:
- * @f: A FILE* stream, must have underlying fd
- *
- * Unix defaults for data preservation after system crash
- * are unspecified, and many systems will eat your data
- * in this situation unless you explicitly fsync().
- *
- * Returns: %TRUE on success, %FALSE on failure, and will set errno()
- */
-static gboolean
-safe_fclose (FILE *f)
-{
- int fd = fileno (f);
- g_assert (fd >= 0);
- if (fflush (f) == EOF)
- return FALSE;
-#ifndef G_OS_WIN32
- if (fsync (fd) < 0)
- return FALSE;
-#endif
- if (fclose (f) == EOF)
- return FALSE;
- return TRUE;
-}
-
-static void
-build_cache (const gchar *path)
-{
- gchar *cache_path, *tmp_cache_path;
-#ifdef G_OS_WIN32
- gchar *bak_cache_path = NULL;
-#endif
- GHashTable *files;
- FILE *cache;
- GStatBuf path_stat, cache_stat;
- struct utimbuf utime_buf;
- GList *directories = NULL;
- int fd;
- int retry_count = 0;
-#ifndef G_OS_WIN32
- mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-#else
- int mode = _S_IWRITE | _S_IREAD;
-#endif
-#ifndef _O_BINARY
-#define _O_BINARY 0
-#endif
-
- tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
-
-opentmp:
- if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
- {
- if (retry_count == 0)
- {
- retry_count++;
- g_remove (tmp_cache_path);
- goto opentmp;
- }
- g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
- exit (1);
- }
-
- cache = fdopen (fd, "wb");
-
- if (!cache)
- {
- g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
- exit (1);
- }
-
- files = g_hash_table_new (g_str_hash, g_str_equal);
- image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
- icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
- string_pool = g_hash_table_new (g_str_hash, g_str_equal);
-
- directories = scan_directory (path, NULL, files, NULL, 0);
-
- if (g_hash_table_size (files) == 0)
- {
- /* Empty table, just close and remove the file */
-
- fclose (cache);
- g_unlink (tmp_cache_path);
- g_unlink (cache_path);
- exit (0);
- }
-
- /* FIXME: Handle failure */
- if (!write_file (cache, files, directories))
- {
- g_unlink (tmp_cache_path);
- exit (1);
- }
-
- if (!safe_fclose (cache))
- {
- g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
- g_unlink (tmp_cache_path);
- exit (1);
- }
- cache = NULL;
-
- g_list_free_full (directories, g_free);
-
- if (!validate_file (tmp_cache_path))
- {
- g_printerr (_("The generated cache was invalid.\n"));
- /*g_unlink (tmp_cache_path);*/
- exit (1);
- }
-
-#ifdef G_OS_WIN32
- if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
- {
- bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
- g_unlink (bak_cache_path);
- if (g_rename (cache_path, bak_cache_path) == -1)
- {
- int errsv = errno;
-
- g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
- cache_path, bak_cache_path,
- g_strerror (errsv),
- cache_path);
- g_unlink (cache_path);
- bak_cache_path = NULL;
- }
- }
-#endif
-
- if (g_rename (tmp_cache_path, cache_path) == -1)
- {
- int errsv = errno;
-
- g_printerr (_("Could not rename %s to %s: %s\n"),
- tmp_cache_path, cache_path,
- g_strerror (errsv));
- g_unlink (tmp_cache_path);
-#ifdef G_OS_WIN32
- if (bak_cache_path != NULL)
- if (g_rename (bak_cache_path, cache_path) == -1)
- {
- errsv = errno;
-
- g_printerr (_("Could not rename %s back to %s: %s.\n"),
- bak_cache_path, cache_path,
- g_strerror (errsv));
- }
-#endif
- exit (1);
- }
-#ifdef G_OS_WIN32
- if (bak_cache_path != NULL)
- g_unlink (bak_cache_path);
-#endif
-
- /* Update time */
- /* FIXME: What do do if an error occurs here? */
- if (g_stat (path, &path_stat) < 0 ||
- g_stat (cache_path, &cache_stat))
- exit (1);
-
- utime_buf.actime = path_stat.st_atime;
- utime_buf.modtime = cache_stat.st_mtime;
-#if GLIB_CHECK_VERSION (2, 17, 1)
- g_utime (path, &utime_buf);
-#else
- utime (path, &utime_buf);
-#endif
-
- if (!quiet)
- g_printerr (_("Cache file created successfully.\n"));
-}
-
-static void
-write_csource (const gchar *path)
-{
- gchar *cache_path;
- gchar *data;
- gsize len;
- gint i;
-
- cache_path = g_build_filename (path, CACHE_NAME, NULL);
- if (!g_file_get_contents (cache_path, &data, &len, NULL))
- exit (1);
-
- g_printf ("#ifdef __SUNPRO_C\n");
- g_printf ("#pragma align 4 (%s)\n", var_name);
- g_printf ("#endif\n");
-
- g_printf ("#ifdef __GNUC__\n");
- g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
- g_printf ("#else\n");
- g_printf ("static const guint8 %s[] = \n", var_name);
- g_printf ("#endif\n");
-
- g_printf ("{\n");
- for (i = 0; i < len - 1; i++)
- {
- if (i %12 == 0)
- g_printf (" ");
- g_printf ("0x%02x, ", (guint8)data[i]);
- if (i % 12 == 11)
- g_printf ("\n");
- }
-
- g_printf ("0x%02x\n};\n", (guint8)data[i]);
-}
-
-static GOptionEntry args[] = {
- { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
- { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don’t check for the existence of index.theme"), NULL },
- { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don’t include image data in the cache"), NULL },
- { "include-image-data", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &index_only, N_("Include image data in the cache"), NULL },
- { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
- { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
- { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
- { NULL }
-};
-
-static void
-printerr_handler (const gchar *string)
-{
- const gchar *charset;
-
- fputs (g_get_prgname (), stderr);
- fputs (": ", stderr);
- if (g_get_charset (&charset))
- fputs (string, stderr); /* charset is UTF-8 already */
- else
- {
- gchar *result;
-
- result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
-
- if (result)
- {
- fputs (result, stderr);
- g_free (result);
- }
-
- fflush (stderr);
- }
-}
-
-
-int
-main (int argc, char **argv)
-{
- gchar *path;
- GOptionContext *context;
-
- if (argc < 2)
- return 0;
-
- g_set_printerr_handler (printerr_handler);
-
- setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
- bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
- context = g_option_context_new ("ICONPATH");
- g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
-
- g_option_context_parse (context, &argc, &argv, NULL);
-
- path = argv[1];
-#ifdef G_OS_WIN32
- path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
-#endif
-
- if (validate)
- {
- gchar *file = g_build_filename (path, CACHE_NAME, NULL);
-
- if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
- {
- if (!quiet)
- g_printerr (_("File not found: %s\n"), file);
- exit (1);
- }
- if (!validate_file (file))
- {
- if (!quiet)
- g_printerr (_("Not a valid icon cache: %s\n"), file);
- exit (1);
- }
- else
- {
- exit (0);
- }
- }
-
- if (!ignore_theme_index && !has_theme_index (path))
- {
- if (path)
- {
- g_printerr (_("No theme index file.\n"));
- }
- else
- {
- g_printerr (_("No theme index file in “%s”.\n"
- "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
- }
-
- return 1;
- }
-
- if (!force_update && is_cache_up_to_date (path))
- return 0;
-
- replace_backslashes_with_slashes (path);
- build_cache (path);
-
- if (strcmp (var_name, "-") != 0)
- write_csource (path);
-
- return 0;
-}